Java 为什么是侦听器列表?

Java 为什么是侦听器列表?,java,list,listener,Java,List,Listener,为什么监听器列表(例如,在Java中,那些使用addXxxListener()和removeXxxListener()来注册和取消注册监听器的监听器列表)称为列表,并且通常实现为列表?a不是更合适吗,因为对于听众来说 无论调用顺序如何(虽然可能有这样的需求,但它们是特殊情况;普通侦听器机制不提供这样的保证),以及 不需要多次注册同一个侦听器(这样做是否会导致调用同一个侦听器1次或N次,或者是一个错误,这是另一个问题) 这只是一个传统问题吗?不管怎么说,集合都是某种隐藏的列表。是否存在性能差异

为什么监听器列表(例如,在Java中,那些使用
addXxxListener()
removeXxxListener()
来注册和取消注册监听器的监听器列表)称为列表,并且通常实现为列表?a不是更合适吗,因为对于听众来说

  • 无论调用顺序如何(虽然可能有这样的需求,但它们是特殊情况;普通侦听器机制不提供这样的保证),以及
  • 不需要多次注册同一个侦听器(这样做是否会导致调用同一个侦听器1次或N次,或者是一个错误,这是另一个问题)

这只是一个传统问题吗?不管怎么说,集合都是某种隐藏的列表。是否存在性能差异?在
列表
中迭代比在
集合
中迭代快还是慢?要么占用更多的内存,要么占用更少的内存?这些差异几乎可以忽略不计。

什么样的设置?是否所有侦听器都应该实现equals和hashCode,以便使用哈希集,还是使用标识哈希集?将侦听器添加到列表中两倍的用例是否值得这么复杂?是否有一种简单的机制可以使集合安全,防止在调用侦听器的处理程序时添加或删除侦听器


可能会有一些性能差异,但肯定会有更复杂的设计,它会强制将“多添加多删除”决策放入库中,而不是将其留给应用程序。

侦听器列表是列表(而不是集合)的一个重要原因还解释了为什么您经常看到它们被向后迭代。一个常见的场景是,当侦听器收到某些更改的通知时,它将自己作为侦听器删除。如果侦听器存储为列表并向前迭代(或存储为集合并以某种不确定的顺序迭代),将其自身作为侦听器删除将导致ConcurrentModificationException


因此,监听器存储为一个列表,并按向后顺序通知。然后,如果一个侦听器在收到通知时将自己从侦听器列表中删除,则不会导致ConcurrentModificationException或移动其他尚未通知的侦听器的索引。

您完全正确。应将侦听器添加到集合中。正如您所说,多次添加侦听器没有意义。此外,如果使用集合,则不会依赖侦听器的顺序。这是最重要的一点:如果你认真对待软件开发和我们发现的每一条引导我们进行更好设计的原则:隔离、独立、责任,那么就不应该依赖它

这里提到的每一个方面(多线程、性能等等)都必须在一开始就从属于自己,但是如果你有充分的理由的话,之后可能会被破坏。我的意思是有很好的理由


顺便说一句:让听者离开自己是一种不好的做法。添加和删除应对称。因此,应该通过注册它的对象删除侦听器。如果你有很多听众参与,你很快就会陷入困境。

“因为这是一个列表?”我认为一个身份散列集是合适的。但是,当然,没有现成的“CopyOnWriteIdentityHashSet”,因此使用common list习惯用法可能更容易获得正确的最终结果。但是:Java 6中确实有一个CopyOnWriteArraySet,它是线程安全的(“通过迭代器进行遍历很快,不会遇到其他线程的干扰。”)。根据“此实现非常适合维护必须防止重复的事件处理程序列表”。@Joonas由于Swing处理程序是在Swing线程中调用的,所以问题总是修改基础集合并使迭代器无效,而不是并发更新。但是,如果您确实需要这些语义,您当然可以使用写集上的副本。这是CopyOnWritexx的一个很酷的功能:修改基础集合不会使迭代器无效。如果您不关心顺序,该怎么办?如果您的侦听器在收到通知后删除自己,您应该关心。否则,你没有因为维护秩序而失去任何东西。如果他们自己移除,我为什么要在意?我认为在大多数情况下,使用集合是可以的。与其他侦听器相比,侦听器通常不应该假设何时调用它们。也许一个技工是解决这个问题的方法?奇怪的是,我找不到它的Java类。如果您正在迭代一组侦听器,通知每个侦听器,并且其中一个侦听器将自己从您当前迭代的侦听器集中移除,那么您可能会得到ConcurrentModificationException。当然,这个问题还有其他解决方案,但将侦听器存储在列表中并按向后顺序通知它们肯定是一个简单的解决方案。哦,您的意思是在调用其函数时删除侦听器。。。是的,这可能会有问题,但您可以通过多种方式来解决这个问题(您的假设很多关于如何实现它,因为可能还涉及多个线程,而且它破坏了您非常想要的整个顺序概念),正如您所写的那样。一种方法是使用并发集。另一种是有另一组等待删除的侦听器,这些侦听器将在对所有侦听器的调用完成后被删除。