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

相关:Java是否有“LinkedConcurrentHashMap”数据结构?


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

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

  1. 维护广告订单。较早的侦听器可能会取消事件,从而阻止将其传递给稍后添加的侦听器。如果使用诸如其迭代器之类的类可能会以错误的顺序返回元素,这将中断。HashSet
  2. 使用 s,以便侦听器列表不会阻止对侦听器进行垃圾回收。WeakReference
  3. 集合是 一个 ,因此会自动删除重复项。Set
  4. 是集合的线程安全快照,不受添加新侦听器的影响。还允许在多个线程上传递事件。(这不是必需的 - 我可以迭代集合的克隆。Iterator

我知道有些课程满足一些但不是所有这些标准。例子:

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

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


答案 1

您可以使用 WeakListeners(参见 http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/WeakListeners.html)和 CopyOnWriteArraySet。

  1. 在事件源中实现方法。remove(ListenerType listener)
  2. 在您的方法中,改为将 WeakListener 添加到集合中:register(SomeListener listener)

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

当真正的侦听器从内存中删除时,弱侦听器将收到通知,并且它将注销自身。(这就是为什么它需要引用源()进行注册的原因。取消注册是通过调用源的 remove 方法使用反射完成的。this


答案 2

我首先要说的是,你们有几个要求在一起没有意义。您正在寻找一个可以删除重复项并支持弱引用的集合,这向我表明侦听器可能会在不确定的时间出现和消失。但是,您希望保持广告订单,并允许一个侦听器取消所有后续通知。对我来说,这听起来像是难以找到的错误配方,我强烈建议重新考虑它。

也就是说,您有一个几乎驱动解决方案的要求:您不希望来自普通迭代器的要求。这意味着您将不得不复制原始列表。在此过程中,您可以检查并删除空引用: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)!”

好吧,是的,你可以。我不知道你打算有多少听众。但是,只要您<100(更有可能<100,000),线性搜索插入和删除的成本就无关紧要了。

从编码的角度来看,更有趣的是如何处理弱引用。您会注意到,在测试引用符为 null 之前,我显式地将其取消引用到变量中。在处理引用对象时,这是至关重要的代码:尽管在对 的两次调用之间收集引用对象的可能性极小,但这是可能的。get()

这让我想到了它本身。您需要创建自己的子类,该子类将覆盖 和 方法以委托给其引用。我以为我刚刚有这样一个班级,但显然不是,所以会把它留给你去实现。WeakReferenceequals()hashCode()