Как реализовать отсортированную таблицу (упорядочить по полю элемента) с помощью java TreeSet?

Я использовал для этого TreeSet, и он работает в стиле каждого снимка. Другими словами, sort Once отображается один раз.

Теперь я хочу реализовать отсортированную таблицу в реальном времени.

Всякий раз, когда происходит изменение значения каких-либо элементов, отсортированная таблица будет соответствующим образом обновляться.

Чтобы сортировка работала для каждого стиля обновления, я попытался удалить элемент и снова добавить его в TreeSet.

quotes.remove(quote);
quotes.add(quote);

Это не работает, потому что мне нужно реализовать логику сортировки в compareTo(), но это нарушает контракт для идентификации объекта, который заставляет работать remove(). TreeSet никогда не вызывает equals() и hashcode(), как описано в Java Doc.

Есть идеи? Пожалуйста, порекомендуйте.

код:

import java.util.TreeSet;

public class TreeSetTest {

    public static void main(String args[]) {        
        TreeSetTest test = new TreeSetTest();
        test.onQuoteUpdate("appl", 1000d);
        test.onQuoteUpdate("msft", 2000d);
        test.onQuoteUpdate("face", 3000d);
        test.printTopStocks();
        test.onQuoteUpdate("msft", 5000d);
        test.printTopStocks();
    }

    private Set<Quote> quotes = new TreeSet<Quote>();

    public void onQuoteUpdate(String symbol, double turnover) {
        final Quote quote = new Quote(symbol, turnover);
        quotes.remove(quote);
        quotes.add(quote);
    }

    public void printTopStocks() {
        System.out.println("--Top Stocks By Turnover--");
        for (final Quote quote : quotes) {
            System.out.println(quote);
        }
    }
    public static class Quote implements Comparable<Quote> {

        private String symbol;

        private double turnover;

        public Quote(String symbol, double turnover) {
            this.symbol = symbol;
            this.turnover = turnover;
        }

        @Override
        public int compareTo(Quote o) {
            return Double.compare(o.turnover, turnover);
//          return symbol.compareTo(o.symbol);
        }

    }
}

Обновление 1:

Как было предложено, я попробовал это:

public static void main(String args[]) {        
    TreeMapTest test = new TreeMapTest();
    test.onQuoteUpdate("appl", 1000d);
    test.onQuoteUpdate("msft", 2000d);
    test.onQuoteUpdate("face", 3000d);
    test.printTopStocks();
    test.onQuoteUpdate("face", 50d);
    test.printTopStocks();
}

    public int compareTo(Quote o) {
        if(o.symbol.equals(symbol)) return 0;
        return Double.compare(o.turnover, turnover);
    }

Метод remove() возвращает false, что в итоге содержит четыре элемента (ожидается 3) в наборе.

--Top Stocks By Turnover--
Quote [symbol=face, turnover=3000.0]
Quote [symbol=msft, turnover=2000.0]
Quote [symbol=appl, turnover=1000.0]
remove symbol face : false
add    symbol face : true
--Top Stocks By Turnover--
Quote [symbol=face, turnover=3000.0]
Quote [symbol=msft, turnover=2000.0]
Quote [symbol=appl, turnover=1000.0]
Quote [symbol=face, turnover=50.0]

Обновление 2:

Я попробовал PriorityQueue, и вот код: https://code.sololearn.com/cb38Eo036c8y/#java

Это не работает, потому что PriorityQueue не хранит элементы по порядку. Порядок работает только при опросе элемента из очереди.

Обновление 3:

Попробовал предложение пользователя 54321, используя пользовательскую коллекцию (см. Ответ ниже). Однако это выглядит не очень хорошо, если есть еще два элемента с одинаковым значением «оборачиваемости».

Мое требование очень обычное. Кажется, ни одна коллекция из JDK не подходит для моего случая.

Обновление 4:

Решение от user54321 подходит для моей временной потребности. https://code.sololearn.com/c14Ybab7AOFm/#java


person Ken Tsang    schedule 03.04.2020    source источник
comment
Пожалуйста, добавляйте только релевантный и минимальный код   -  person Omri Attiya    schedule 03.04.2020
comment
Я думаю, что ваш класс Quote должен также переопределить метод equals(Object), который он наследует от своего суперкласса java.lang.Object. И если вы переопределяете метод equals(Object), рекомендуется также переопределить метод hashCode().   -  person Abra    schedule 04.04.2020


Ответы (2)


Удалил мой ранее добавленный ответ. Похоже, что для сценария используется неправильная структура данных.

Вот почему.

Когда элемент добавляется или удаляется, TreeSet выполняет двоичный поиск среди доступных элементов, используя compareTo().

В твоем случае,

После добавления первых трех элементов набор выглядит так.
[{appl, 1000d}, {msft, 2000d}, {face, 3000d}]

Теперь, когда вы пытаетесь удалить элемент {face, 50d},

Он начинает поиск с {msft, 2000d}, по результату compareTo() определяет, что {face, 50d} должно стоять перед {msft, 2000d}.

И продолжает поиск в направлении начала элементов (проверка с помощью {appl, 1000d} далее).

Поскольку поиск не находит {face, 3000d}, этот элемент остается без удаления.

Затем, когда вы добавляете элемент {face,50}, происходит аналогичный поиск, и, поскольку поиск не находит {face, 3000}, он добавляет {face, 50} в начало.

Теперь набор выглядит так.
[{face, 50}, {appl, 1000d}, {msft, 2000d}, {face, 3000d}]

Теперь проблема в том, что compareTo() не может учитывать как символ, так и оборот для разумной сортировки. TreeSet можно использовать для получения отсортированной коллекции уникальных элементов.

Если вам нужно получить отсортированную коллекцию различных объектов с определенными критериями сортировки, в данном случае значение оборота, вы можете использовать PriorityQueue

Обновление: использование списка и набора в пользовательской структуре данных

Проблема здесь в том, что мы должны поддерживать два условия. 1. Символ должен быть уникальным 2. Коллекция должна быть отсортирована по обороту

compareTo() в Quote можно проверять по одному, а не оба. Так что в этом случае нам, возможно, придется пойти на пользовательскую структуру данных.

Сначала используйте только turnover в compareTo();

    @Override
    public int compareTo(Quote o) {
        return Double.compare(o.turnover, turnover);
    }

Затем реализуйте пользовательскую структуру данных. Обратите внимание, что мы используем HashSet для отслеживания только символа. Использование списка, чтобы можно было сохранить повторяющиеся значения оборота.

static class QuoteCollection {
    Set<String> symbols = new HashSet<>();
    List<Quote> quotes = new LinkedList<>();

    public void onQuoteUpdate(Quote q) {

        if (symbols.contains(q.getSymbol())) {
            // this requires quotes.equals() to be implemented
            quotes.remove(q);
        } else {
            symbols.add(q.getSymbol());
        }
        insertToCollection(q);
    }

    // inserting at correct position to remain sorted
    private void insertToCollection(Quote q) {
        int index = Collections.binarySearch(quotes, q);
        if (index < 0)
            index = ~index; // bitwise compliment to find insert position if it is not available in the list
        quotes.add(index, q);
    }

    public List<Quote> getQuotes() {
        return quotes;
    }
}

Затем используйте его в main(). Обратите внимание, что функция printTopStocks() была немного изменена.

public static void main(String args[]) {
    Main test = new Main();
    QuoteCollection quoteCollection = new QuoteCollection();
    quoteCollection.onQuoteUpdate(new Quote("appl", 1000d));
    quoteCollection.onQuoteUpdate(new Quote("msft", 2000d));
    quoteCollection.onQuoteUpdate(new Quote("face", 3000d));
    test.printTopStocks(quoteCollection.getQuotes());
    quoteCollection.onQuoteUpdate(new Quote("face", 50d));
    test.printTopStocks(quoteCollection.getQuotes());
}
public void printTopStocks(List<Quote> quotes) {
    System.out.println("--Top Stocks By Turnover--");
    for (final Quote quote : quotes) {
        System.out.println(quote);
    }
}

Этот подход предполагает дублирование данных. Однако отсортированная коллекция может поддерживаться с линейной временной сложностью (поскольку она использует «List.remove()»).

person user54321    schedule 03.04.2020
comment
PriortyQueue не хранит элементы по порядку. Порядок правильный только тогда, когда вы выполняете элемент poll() из очереди. Смотрите мое обновление выше. - person Ken Tsang; 04.04.2020
comment
@KenTsang сделал обновление. Похоже, что для этого требуется специальная структура данных. - person user54321; 04.04.2020
comment
Большое спасибо за ваш ответ. Тем не менее, в некоторых случаях это все еще не работает, если есть две акции с одинаковым значением оборота. - person Ken Tsang; 04.04.2020
comment
Да, когда значения оборота одинаковы, он не добавит новый объект в набор, поскольку compareTo() возвращает 0. Но конечный результат остается таким же, как и требовалось. - person user54321; 04.04.2020
comment
Я сделал это. Есть три элемента и добавьте еще один (с таким же значением оборота, как один из трех). Окончательный TreeSet содержит только 3 элемента (ожидается 4 элемента). Пожалуйста, проверьте это: code.sololearn.com/cMl0C7oj90QS/#java - person Ken Tsang; 04.04.2020
comment
Извините, что я пропустил этот случай в реализации. Чтобы обойти это, похоже, нам нужно использовать комбинацию List и Set в QuoteCollection. Обновил ответ :) - person user54321; 04.04.2020
comment
Давайте продолжим обсуждение в чате. - person Ken Tsang; 04.04.2020

Пара моментов:

  1. Попытка удалить элементы, даже если вы добавляете их в первый раз.

  2. При обновлении вы пытаетесь удалить новый элемент, которого нет в TreeSet. final Quote quote = new Quote(symbol, turnover); здесь вы создаете новый элемент, который Quote("face","50d") не существует, когда вы вызываете quotes.remove(quote);

Ниже приведен один из способов его решения, я жестко кодирую oldQuote, чтобы он был коротким, но вы можете обновить его:

   public void onAdd(String symbol, double turnover) {
            final Quote quote = new Quote(symbol, turnover);

            quotes.remove(quote);
            quotes.add(quote);
        }

     public void onQuoteUpdate(String symbol, double turnover) {
                final Quote newQuote = new Quote(symbol, turnover);
                final Quote oldQuote = new Quote("face", 3000d);
                quotes.remove(oldQuote);
                quotes.add(quote);
            } 

     public static void main(String args[]) {        
                TreeSetTest test = new TreeSetTest();
                test.onAdd("appl", 1000d);
                test.onAdd("msft", 2000d);
                test.onAdd("face", 3000d);
                test.printTopStocks();
                test.onQuoteUpdate("face", 50d);
                test.printTopStocks();
            } 
person Datta Diware    schedule 03.04.2020