85

我有一个使用 Comparable<> 定义“自然排序顺序”的对象。这些存储在 TreeSets 中。

除了删除和重新添加对象之外,当用于定义排序顺序的成员更新时,是否有另一种方法来更新排序?

4

7 回答 7

18

正如其他人所指出的,没有内置的方法。但是您始终可以使用您选择的构造函数对该 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()方法。

于 2010-04-05T22:00:34.110 回答
6

我有一个类似的问题,找到了这个线程和 tucuxi 的答案(谢谢!)基于我实现了我自己的UpdateableTreeSet. 我的版本提供了

  • 迭代这样一个集合,
  • 从循环中安排(延迟)元素更新/删除
  • 无需创建集合的临时副本,最后
  • 在循环结束后将所有更新/删除作为批量操作进行。

UpdateableTreeSet对用户隐藏了很多复杂性。除了延迟的批量更新/删除之外,tucuxi 所示的单元素更新/删除在类中仍然可用。

2012-08-07 更新:该类在一个小的GitHub 存储库中可用,包括一个介绍性的 README,其中包含示意性示例代码以及显示如何(不)更详细地使用它的单元测试。

于 2012-06-23T11:55:31.193 回答
3

如果你真的需要使用 a Set,那么你就不走运了,我想。

不过,我将使用通配符 - 如果您的情况足够灵活,可以使用 aList而不是 a Set,那么您可以使用按需Collections.sort()重新排序。如果订单不需要改变太多List,这应该是高效的。List

于 2010-04-05T17:10:42.447 回答
1

唯一的内置方式是删除并重新添加。

于 2010-04-05T17:05:12.423 回答
1

它有助于了解您的对象是否会以小增量或大增量进行更改。如果每次更改都非常小,您最好将数据放入您保持排序的列表中。为此,您必须

  1. binarySearch 查找元素的索引
  2. 修改元素
  3. 当元素大于其右侧邻居时,将其与右侧邻居交换
  4. 或者如果这没有发生:当元素小于其左侧邻居时,将其与左侧邻居交换。

但是您必须确保没有人可以在不通过“您”的情况下更改元素。

编辑:还有!Glazed Lists 对此有一些支持:

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

于 2010-04-05T22:36:04.087 回答
1

当我尝试实现类似于苹果 iPhone 滚轮滚动的动态滚动窗格时,我查找了这个问题。中的项目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());
    }
}

可能需要一些时间才能绑定到 JavaFX 平台的DoubleExpressionrunLater 任务中,这就是索引包含在这个包装类中的原因。

由于scrollOffset总是根据用户在滚轮上的滚动位置而变化,因此我们需要一种更新方法。通常顺序总是相同的,因为偏移量是相对于项目索引位置的。索引永远不会改变,但偏移量可能是负数或正数,具体取决于项目与当前 vValue 或 hValue 属性的相对距离ScrollPane

仅在需要时按需更新,只需按照图库西上述答案的指导进行操作即可。

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

其中verticalOffsets是一个TreeSet<ItemOffset>。如果每次调用此更新片段时都打印出集合,您将看到它已更新。

于 2016-03-23T15:07:27.920 回答
-1

我不认为有一种开箱即用的方式来做到这一点。

您可以使用观察者模式,每当您更改元素内的值时通知树集,然后删除并重新插入它。

通过这种方式,您可以隐式地保持列表排序,而无需手动进行。当然,这种方法需要TreeSet通过修改插入的行为来扩展(在刚刚添加的项目上设置观察/通知机制)

于 2010-04-05T17:04:21.270 回答