适用于Java中事件侦听器的集合类

时间:2010-01-15 15:34:18

标签: java collections concurrency weak-references event-listener

相关: Does java have a "LinkedConcurrentHashMap" data structure?


我正在寻找一个集合类来保存对事件监听器的引用。

理想情况下,我希望集合具有以下属性(按优先级顺序):

  1. 维护广告订单。较早的侦听器可能会取消该事件,从而阻止它被传递给稍后添加的侦听器。如果使用诸如HashSet之类的类,其迭代器可能以错误的顺序返回元素,则会中断。
  2. 使用WeakReference s,以便侦听器列表不会阻止侦听器被垃圾回收。
  3. 该集合为Set,因此会自动删除重复项。
  4. Iterator是集合的线程安全快照,不受添加新侦听器的影响。还允许在多个线程上传递事件。 (这不是必不可少的 - 我可以迭代一下该集的克隆。)
  5. 我知道有些课程满足一些但并非所有这些标准。例子:

    • java.util.LinkedHashSet(#1和#3)
    • java.util.WeakHashMap,由Collections.newSetFromMap(#2和#3)
    • 包裹
    • javax.swing.event.EventListenerList(需要一些额外的同步)(#1和#4)
    • java.util.concurrent.CopyOnWriteArraySet(#1,#3和#4)

    但#1和#2都没有。这样的类是否存在于某个库中?

5 个答案:

答案 0 :(得分:7)

我首先要说的是,你有一些没有意义的要求。您正在寻找一个删除重复项并支持弱引用的集合,这表明听众可能会在不确定的时间出现和消失。但是,您希望维护插入顺序,并允许一个侦听器取消所有后续通知。对我而言,这听起来像是难以发现的错误的秘诀,我强烈建议重新考虑它。

也就是说,你有一个几乎驱动解决方案的要求:你不希望ConcurrentModificationException可能来自正常的迭代器。这意味着您将不得不复制原始列表。在此过程中,您可以检查并删除空引用:

// 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)!”

嗯,是的,你可以。我不知道你打算有多少听众。但只要你是&lt; 100(并且更可能<100,000),线性搜索插入和移除的成本并不重要。

从编码角度来看,更有趣的是如何处理弱参考。你会注意到我在测试引用为null之前,明确地将它解引用到变量中。在处理引用对象时,这是非常重要的代码:尽管在两次调用get()之间收集引用的可能性极小,但这是可能的。

这将我带到WeakReference本身。您需要创建自己的子类来覆盖equals()hashCode()方法以委托给它的引用。我以为我只是躺在这样的课堂上,但显然不是,所以会留给你实施。

答案 1 :(得分: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));

  3. 当从内存中删除真实侦听器时,将通知弱侦听器,并且它将取消注册自身。 (这就是它需要对源(this)进行注册的原因。)通过调用方法删除源来使用反射完成取消注册。

答案 2 :(得分:1)

Set是与侦听器一起使用的正确集合。

如果您依赖于侦听器的插入顺序,则您的设计会被破坏。它忽略了听众被其他听众孤立和独立的观点。使用集而不是列表。

如果您依赖WeakReferences,您的设计就会被打破。删除添加它的同一对象中的侦听器。该SYMMETRY支持READABILITY和MAINTAINABILITY。要解决具有弱引用的侦听器的forgotton取消订阅的编程错误,只能隐藏问题。

如果您将侦听器集合提供给其他对象而不是观察对象,那么您的设计就会被破坏。将Set设置为private以支持ENCAPSULATION。

如果覆盖侦听器的equals和hashcode,则设计会被破坏。它隐藏了不必要的函数调用的问题。改为防止不必要的呼叫。在所有重写的equals和侦听器的哈希码之后都不是必需的。

在MULTITHREADING环境中,在资源“侦听器”上放置一个MONITOR,同时添加,删除或迭代它。您可以在迭代之前创建一个DEFENSIVE COPY以避免ConcurrentModificationException。然后迭代不必是SYNCHRONIZED,但复制操作应该是。

任何其他要求必须进行调整或重新制定以符合这些陈述。任何其他做法都会导致无法维护的代码,内存泄漏,因为缺乏隔离,独立性,封装和清晰度。

答案 3 :(得分:0)

您可以将每个侦听器引用包装在 WeakReference 中,然后使用 CopyOnWriteArraySet

答案 4 :(得分:0)

您可以扩展WeakReference以覆盖equals和hashcode,然后您可以在LinkedHashSet中使用它们。