14

関連: Javaには「LinkedConcurrentHashMap」データ構造がありますか?


イベント リスナーへの参照を保持するコレクション クラスを探しています。

理想的には、コレクションに次のプロパティを持たせたいと思います (優先度順):

  1. 挿入順序を維持します。以前のリスナーはイベントをキャンセルし、後で追加されたリスナーに配信されないようにする場合があります。HashSetイテレータが間違った順序で要素を返すようなクラスを使用すると、これは壊れます。
  2. s を使用WeakReferenceして、リスナー リストがリスナーのガベージ コレクションを妨げないようにします。
  3. コレクションは であるSetため、重複は自動的に削除されます。
  4. これIteratorはコレクションのスレッドセーフなスナップショットであり、新しいリスナーの追加による影響を受けません。イベントを複数のスレッドに配信することもできます。(これは必須ではありません。代わりに、セットのクローンを反復処理できます。)

これらの基準のすべてではなく一部を満たすいくつかのクラスを認識しています。例:

  • java.util.LinkedHashSet(#1 と #3)
  • java.util.WeakHashMapCollections.newSetFromMap(#2 と #3)でラップ
  • javax.swing.event.EventListenerList(追加の同期が必要です) (#1 と #4)
  • java.util.concurrent.CopyOnWriteArraySet(#1、#3、#4)

しかし、#1と#2の両方には何もありません。このようなクラスはどこかのライブラリに存在しますか?

4

5 に答える 5

7

まず、一緒に意味をなさない要件がいくつかあると言うところから始めましょう。重複を削除し、弱い参照をサポートするコレクションを探しています。これは、不確定な時間にリスナーが表示されたり消えたりする可能性があることを示しています。ただし、挿入順序を維持し、1 つのリスナーが後続のすべての通知をキャンセルできるようにする必要があります。私には、これは見つけにくいバグのレシピのように思えます。再考することを強くお勧めします。

ConcurrentModificationExceptionとはいえ、ソリューションを推進する要件が 1 つあります。それは、通常のイテレータから発生する可能性のある を望まないということです。つまり、元のリストをコピーする必要があります。途中で、空の参照を確認して削除できます。

// the master list
List<WeakReference<MyListener>> _list = new ArrayList<WeakReference<MyListener>>();

// inside your send-notification method
List<MyListener> toNotify = new ArrayList<MyListener>(_list.size());
Iterator<WeakReference<MyListener>> itx = _list.iterator();
while (itx.hasNext())
{
    WeakReference<MyListener> ref = itx.next();
    MyListener lsnr = ref.get();
    if (lsnr != null)
        toNotify.add(lsnr);
    else
        itx.remove();
}

// now iterate "toNotify" and invoke the listeners

あなたはおそらく今、「リスト! これは線形データ構造だ! 使えない。挿入は O(N) だ!」と言って、おかしくなっているでしょう。

はい、できます。何人のリスナーを予定しているかわかりません。しかし、100 未満 (そしておそらく 100,000 未満) である限り、挿入と削除の線形検索のコストは問題になりません。

コーディングの観点から見ると、さらに興味深いのは、弱参照をどのように扱うかということです。null の指示対象をテストする前に、変数に明示的に逆参照していることに注意してください。これは、参照オブジェクトを扱う際に非常に重要なコードですget()

WeakReferenceそれは私をそれ自体に導きます。equals()およびhashCode()メソッドをオーバーライドして参照先に委譲する独自のサブクラスを作成する必要があります。私はちょうどそのようなクラスが横たわっていると思っていましたが、どうやらそうではないので、実装するために残します。

于 2010-01-18T13:37:31.527 に答える
7

WeakListeners ( http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/WeakListeners.htmlを参照) と CopyOnWriteArraySet を使用できます。

  1. remove(ListenerType listener)イベント ソースにメソッドを実装します。
  2. register(SomeListener listener)メソッドで、代わりに WeakListener をコレクションに追加します。

    listenerCollection.put((ListenerType)WeakListeners.create ( ListenerType.class, listener, this));

実際のリスナーがメモリから削除されると、弱いリスナーに通知され、自身の登録が解除されます。(これが、登録のためにソース ( ) への参照が必要な理由thisです。) 登録解除は、ソースのメソッド remove を呼び出すことによってリフレクションを使用して行われます。

于 2010-01-18T15:34:39.400 に答える
1

Set は、リスナーで使用する適切なコレクションです。

リスナーの挿入順序に依存すると、設計が壊れます。リスナーが他のリスナーから分離され、独立しているという点が欠けています。リストの代わりにセットを使用します。

WeakReferences に依存している場合、設計が壊れています。追加した同じオブジェクト内のリスナーを削除します。この SYMMETRY は、READABILITY と MAINTAINABILITY をサポートします。弱い参照を持つリスナーの忘れられたサブスクリプション解除のプログラミングエラーを解決するには、問題を隠すだけです。

リスナーのコレクションを監視対象オブジェクト以外のオブジェクトに提供すると、設計が壊れます。ENCAPSULATION をサポートするために Set を非公開にしてください。

リスナーの equals と hashcode をオーバーライドすると、設計が壊れます。不要な関数呼び出しの問題を隠します。代わりに、不要な呼び出しを防ぎます。結局、リスナーの equals と hashcode をオーバーライドする必要はありません。

MULTITHREADING 環境では、追加、削除、または反復処理中にリソース「リスナー」に MONITOR を配置します。ConcurrentModificationException を回避するために、反復する前に DEFENSIVE COPY を作成できます。その場合、反復を同期する必要はありませんが、コピー アクションは同期する必要があります。

その他の要件は、これらのステートメントに一致するように調整または再構築する必要があります。他の方法では、分離、独立性、カプセル化、および明確さが欠如しているため、メンテナンス不能なコードやメモリ リークが発生します。

于 2015-04-27T18:27:04.150 に答える
0

各リスナー参照をWeakReferenceでラップしてから、 CopyOnWriteArraySetを使用できます。

于 2010-01-15T15:49:29.547 に答える
0

WeakReference を拡張して equals と hashcode をオーバーライドすると、それらを LinkedHashSet で使用できます。

于 2010-01-18T12:49:07.013 に答える