2010-04-05 7 views
63

У меня есть объект, который определяет «естественный порядок сортировки», используя Comparable <>. Они хранятся в TreeSets.сохранение сортировки TreeSet как значение объекта изменения

Помимо удаления и повторного добавления объекта, существует ли другой способ обновления сортировки, когда обновляются члены, которые используются для определения порядка сортировки?

+6

+1 nice question – skaffman

+0

Это просто болезненное любопытство, или вы ищете конкретную выгоду, скажем, в простоте или простоте кода? – greim

+2

Я искал простой неинвазивный способ поддержания порядка сортировки коллекции, поскольку события обновляют объекты внутри нее. Изменение членства имеет побочные эффекты для других потребителей коллекции. IMO, запускающий курортный элемент, кажется базовой базой для сортированной коллекции. – Stevko

ответ

11

Как уже отмечалось, встроенного способа нет. Но вы всегда можете создать подкласс, который TreeSet, с конструктором (ы) выбора, а также добавлять в требуемой функциональности:

public class UpdateableTreeSet<T extends Updateable> extends TreeSet<T> { 

    // definition of updateable 
    interface Updateable{ void update(Object value); } 

    // constructors here 
    ... 

    // 'update' method; returns false if removal fails or duplicate after update 
    public boolean update(T e, Object value) { 
     if (remove(e)) { 
      e.update(value); 
      return add(e); 
     } else { 
      return false; 
     } 
    } 
} 

С тех пор, вы должны позвонить ((UpdateableTreeSet)mySet).update(anElement, aValue) обновить сортировочное значение и сама сортировка , Это требует, чтобы вы реализовали дополнительный метод update() в вашем объекте данных.

+4

Это не сработает. remove (e) не сможет найти элемент, если его значение уже изменилось. –

+1

Голли, ты прав. Редактирование, чтобы исправить ... – tucuxi

+0

To-voter - почему? – tucuxi

-1

Я не думаю, что есть готовый способ сделать это.

Вы можете использовать шаблон наблюдателя , который уведомляет деревья, когда вы меняете значение внутри элемента, а затем удаляет и повторно вставляет его.

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

+3

Я не думаю, что это сработает. Если значение элемента изменяется таким образом, что влияет на порядок сортировки, вполне вероятно, что вы не сможете найти его в дереве * или удалить его. –

0

Только встроенный способ заключается в удалении и повторном добавлении.

2

Если вам действительно нужно использовать Set, то вам не повезло, я думаю.

Я собираюсь бросить в качестве шаблона, хотя - если ваша ситуация является достаточно гибкой, чтобы работать с List вместо Set, то вы можете использовать Collections.sort() для пересортируйте List по требованию. Это должно быть выполнено, если заказ List не должен сильно меняться.

+1

Не так много преимуществ для этого - задание гарантирует быстрый поиск, вставку и удаление, в то время как для списка List необходимо сначала использовать Collections.binarySearch, чтобы найти элемент.Лучше придерживаться удаления и повторной установки. – tucuxi

+0

Я понимаю, что именно поэтому я сказал «если ваша ситуация достаточно гибкая». Требования к производительности * могут быть достаточно свободными, чтобы «Список» был практичным. – skaffman

+1

Бинарный поиск @tucuxi в списке - O (log n). Поиск в TreeSet? Также O (log n). –

1

Это помогает узнать, будут ли ваши объекты изменяться небольшими приращениями или большими. Если каждое изменение очень мало, вы бы очень хорошо поместили свои данные в список, который вы сохранили. Чтобы сделать это, вы должны

  1. BinarySearch найти индекс элемента
  2. изменить элемент
  3. в то время как элемент больше, чем его правого сосед, поменять его с правым соседом
  4. или если этого не произошло: в то время как элемент меньше его левого соседа, замените его своим левым соседом.

Но вы должны убедиться, что никто не может изменить элемент, не пройдя через «вы», чтобы сделать это.

EDIT: Также! Глазированные Списки имеет некоторую поддержку только это:

http://publicobject.com/glazedlists/glazedlists-1.5.0/api/ca/odell/glazedlists/ObservableElementList.html

4

у меня была аналогичная проблема, нашел эту тему и ответ на белый дельфин (в спасибо!), на основе которого я реализовал свой собственный UpdateableTreeSet. Моя версия предоставляет средства для

  • перебрать такой набор,
  • график обновление (отложенный) элемента/абсорбция в цикле
  • без необходимости создавать временную копию набора и, наконец,
  • делать все обновления/удаления как объемную операцию после окончания цикла.

UpdateableTreeSet скрывает от пользователя сложную сложность. В дополнение к отложенным массовым обновлениям/абзацам одноэлементное обновление/удаление, как показано tucuxi, по-прежнему остается доступным в классе.

Обновление 2012-08-07: Класс доступен в небольшом количестве GitHub repository, включая вводный README со схематическим образцовым кодом, а также модульные тесты, показывающие, как (не) использовать его более подробно.

+0

Мне нравится идея - это просто предполагает размещение массива «отложенного обновления» внутри UpdateableTreeSet, а затем вызов «do-offferred-updates», чтобы сначала удалить все обновления, а затем снова вставить их. – tucuxi

+0

Как следует из моего примера кода, это то, что я сделал. Это не просто идея, это реальная реализация. Вы следили за ссылкой на исходный код и также проверили другие файлы в проекте, чтобы узнать, как класс используется в более сложных случаях? – kriegaex

+0

Нет необходимости - идея достаточно ясна, и хотя ваше объяснение несколько чрезмерно, я проголосовал за это как полезное. – tucuxi

1

Я искал эту проблему, когда пытался реализовать кинетическую панель прокрутки, похожую на свитки для iPhone Apple. Элементы в TreeSet являются этим классом:

/** 
* Data object that contains a {@code DoubleExpression} bound to an item's 
* relative distance away from the current {@link ScrollPane#vvalueProperty()} or 
* {@link ScrollPane#hvalueProperty()}. Also contains the item index of the 
* scrollable content. 
*/ 
private static final class ItemOffset implements Comparable<ItemOffset> { 

    /** 
    * Used for floor or ceiling searches into a navigable set. Used to find the 
    * nearest {@code ItemOffset} to the current vValue or hValue of the scroll 
    * pane using {@link NavigableSet#ceiling(Object)} or 
    * {@link NavigableSet#floor(Object)}. 
    */ 
    private static final ItemOffset ZERO = new ItemOffset(new SimpleDoubleProperty(0), -1); 

    /** 
    * The current offset of this item from the scroll vValue or hValue. This 
    * offset is transformed into a real pixel length of the item distance from 
    * the current scroll position. 
    */ 
    private final DoubleExpression scrollOffset; 

    /** The item index in the list of scrollable content. */ 
    private final int index; 

    ItemOffset(DoubleExpression offset, int index) { 
     this.scrollOffset = offset; 
     this.index = index; 
    } 

    /** {@inheritDoc} */ 
    @Override 
    public int compareTo(ItemOffset other) { 
     double d1 = scrollOffset.get(); 
     double d2 = other.scrollOffset.get(); 

     if (d1 < d2) { 
      return -1; 
     } 
     if (d1 > d2) { 
      return 1; 
     } 

     // Double expression has yet to be bound 
     // If we don't compare by index we will 
     // have a lot of values ejected from the 
     // navigable set since they will be equal. 
     return Integer.compare(index, other.index); 
    } 

    /** {@inheritDoc} */ 
    @Override 
    public String toString() { 
     return index + "=" + String.format("%#.4f", scrollOffset.get()); 
    } 
} 

DoubleExpression может занять некоторое время, чтобы быть связанным в runLater задаче платформы JavaFX, поэтому индекс входят в этом классе обертки.

Поскольку scrollOffset всегда меняется в зависимости от положения прокрутки пользователя на колесе прокрутки, нам нужен способ обновления. Обычно порядок всегда один и тот же, поскольку смещение относительно позиции позиции элемента. Индекс никогда не изменяется, но смещение может быть отрицательным или положительным в зависимости от относительного расстояния элементов от текущего значения vValue или hValue для ScrollPane.

Чтобы обновить по запросу только при необходимости, просто следуйте указаниям вышеприведенного ответа Tucuxi.

ItemOffset first = verticalOffsets.first(); 
verticalOffsets.remove(first); 
verticalOffsets.add(first); 

где verticalOffsets является TreeSet<ItemOffset>. Если вы выполняете печать из , установленного каждый раз, когда вызывается этот фрагмент обновления, вы увидите, что он обновлен.