保持TreeSet排序为对象更改值

时间:2010-04-05 17:00:24

标签: java collections refresh treeset sortedset

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

除了删除和重新添加对象之外,还有另一种方法可以在用于定义排序顺序的成员更新时更新排序吗?

7 个答案:

答案 0 :(得分:14)

正如其他人所说,没有内置方式。但是你总是可以使用你选择的构造函数对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()方法。

答案 1 :(得分:5)

我遇到了类似的问题,找到了这个帖子和tucuxi的回答(谢谢!),基于此我实现了自己的UpdateableTreeSet。我的版本提供了

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

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

更新2012-08-07:该课程有点GitHub repository,包括带有原理图示例代码的介绍性自述文件以及显示如何(不)更详细地使用它的单元测试。

答案 2 :(得分:3)

如果你真的需要使用Set,那么我认为你运气不好。

我打算使用通配符 - 如果您的情况足够灵活,可以使用List代替Set,那么您可以使用Collections.sort()来重新设置 - 按需排序List。如果List订单不需要进行太多更改,那么这应该是高效的。

答案 3 :(得分:1)

有助于了解您的对象是以小增量还是大幅度变化。如果每个更改都非常小,那么您可以将数据放在一个保持排序的列表中。要做到这一点,你必须

  1. binarySearch查找元素的索引
  2. 修改元素
  3. 当元素大于其右手邻居时,将其与右手邻居交换
  4. 或者如果没有发生:当元素小于其左手邻居时,将其与其左手邻居交换。
  5. 但是你必须确保没有人能够通过“你”来改变元素。

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

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

答案 4 :(得分:1)

当我尝试实现类似于Apple 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());
    }
}

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

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

要仅在需要时更新 ,只需按照Tucuxi的上述答案的指导。

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

其中 verticalOffsets TreeSet<ItemOffset>。如果你打印了 每次调用此更新代码段时,您都会看到它已更新。

答案 5 :(得分:0)

只有内置方式才能删除并重新添加。

答案 6 :(得分:-1)

我认为没有开箱即用的方法。

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

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