为什么EventListenerList在fireFooXXX()中向后遍历?

我不明白这段代码的基本原理,取自javax.swing.event.EventListenerList docs:

protected void fireFooXXX() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==FooListener.class) { // Lazily create the event: if (fooEvent == null) fooEvent = new FooEvent(this); ((FooListener)listeners[i+1]).fooXXX(fooEvent); } } } 
  1. 为什么列表会向后移动?
  2. 为什么只召唤每一个听众?

事件触发在javax.swing.tree.DefaultTreeModel中以这种方式实现,所以显然我只是没有得到什么。

  1. 可能是性能方面的考虑因素:向后迭代速度更快,因为与0进行比较是一个单一的机器代码指令 – 许多以前的C程序员已经根深蒂固,尽管它现在相当无关紧要。 请注意,无论如何都无法保证听众的通知顺序。
  2. 查看类的其余部分 – 它还存储侦听器的类型以提供类型安全性。

1。

在Swing Hacks项目#94中描述了遍历侦听器时可能遇到的一个问题,如果其中一个在fooXXX()的实现中将其自身移除为侦听器,则会发生这种问题。

考虑一下这个监听器,它可能在收到事件后自行删除:

 public class FooListener implements EventListener { private int i; public FooListener(int i) { this.i = i; } public fooXXX(FooEvent foo) { System.out.println(i); if (i == 1) { ((FooEventSource)foo.getSource()).removeListener(this); } } } 

和侦听器遍历的这个实现:

 public void fireFooXXX() { for (int i=0; i 

现在假设我们创建了许多这样的监听器:

 fooEventSource.addListener(new FooListener(0)); fooEventSource.addListener(new FooListener(1)); fooEventSource.addListener(new FooListener(2)); fooEventSource.addListener(new FooListener(3)); 

触发事件将提供以下输出:

 0 1 3 

我们将通过索引从0到3循环遍历侦听器。在索引1处,侦听器将自己从侦听器的内部数组中移除,从而使侦听器2和3向下移动到索引1和2.循环继续索引2现在包含监听器3.已跳过监听器2。

通过向后迭代,可以消除此问题,因为删除侦听器只会移动已调用的侦听器的索引。

EventListenerList没有此问题,因为add()和remove()方法是copy-on-write,并且建议用法中的侦听器遍历在循环之前对getListenerList()返回的侦听器列表实例进行操作。

关于它的更多讨论可以在这个post中找到,其原因似乎归结为以下之一:

  • 性能

  • 事件排序(最后添加的侦听器将是第一个被通知的)


2。

akf和Michael Borgwardt已经回答说EvenListenerList除了监听器之外还存储了监听器类型。 我想这是因为它使单个EventListenerList可以处理不同类型的侦听器。

回答#2:调用每个第二个侦听器,因为EventListenerList使用的数组被设置为一个Listener-Type,Listener-Instance对的数组。