web-dev-qa-db-ja.com

オブジェクトが値を変更してもTreeSetのソートを維持する

Comparable <>を使用して「自然な並べ替え順序」を定義するオブジェクトがあります。これらはTreeSetsに保存されています。

オブジェクトを削除して再追加する以外に、ソート順の定義に使用されるメンバーが更新されたときにソートを更新する別の方法はありますか?

69
Stevko

他の人が指摘したように、組み込みの方法はありません。ただし、選択したコンストラクタを使用して、常に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()メソッドをデータオブジェクトに実装する必要があります。

14
tucuxi

同様の問題があり、このスレッドとtucuxiの答え(ありがとう!)に基づいて、独自のUpdateableTreeSetを実装しました。私のバージョンは

  • そのようなセットを反復処理し、
  • ループ内から(遅延)要素の更新/削除をスケジュールする
  • セットの一時的なコピーを作成することなく、最終的に
  • ループが終了した後、すべての更新/削除を一括操作として実行します。

UpdateableTreeSetは、ユーザーから多くの複雑さを隠します。一括更新/削除の遅延に加えて、tucuxiで示される単一要素の更新/削除は、引き続きクラスで使用できます。

更新2012-08-07:このクラスは、少しだけ利用可能です GitHubリポジトリ 導入を含むREADME回路図サンプルコードと方法を示す単体テスト(ない))より詳細に使用します。

5
kriegaex

Setを本当に使用する必要がある場合は、運が悪いと思います。

ただし、ワイルドカードを使用します-Listの代わりにSetを使用するのに十分な柔軟性がある場合は、Collections.sort()を使用してListをオンデマンドで再ソートします。 List順序をあまり変更する必要がない場合、これはパフォーマンスが高いはずです。

3
skaffman

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プロパティからのアイテムの相対的な距離に応じて、負または正になります。

必要な場合のみオンデマンドを更新するには、Tucuciによる上記の回答のガイダンスに従ってください。

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

ここで、verticalOffsetsTreeSet<ItemOffset>。この更新スニペットが呼び出されるたびにセットを印刷すると、更新されていることがわかります。

1
ghostNet

オブジェクトが少しずつ変化するか、大きく変化するかを知るのに役立ちます。各変更が非常に小さい場合は、並べ替えておくリストにデータを入れると非常にうまくいきます。これを行うには、する必要があります

  1. binarySearchは、要素のインデックスを見つけます
  2. 要素を修正する
  3. 要素は右隣よりも大きいが、右隣と交換する
  4. または、それが起こらなかった場合:要素がその左隣よりも小さい間、その左隣と交換します。

ただし、「あなた」を経由せずに要素を変更できないようにする必要があります。

EDIT:また! Glazed Listsは、これだけをサポートしています。

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

1

方法で構築されているのは、削除して追加し直すことだけです。

0
Robby Pond