问题 Java中侦听器的正确(和最佳)集合类型


我只是想在我的一个类中引入一个小的观察者模式(听众),我想使用最佳实践方法。

我的监听器界面:

public interface ExpansionListener {
    void expanded();
    void collapsed();
}

因此,我想保留一个听众列表

private List listener; // What kind of list should I take?

和两种方法 addListener(ExpansionListener l) 和 removeListener(ExpansionListener l)

现在,我的 题: 我应该拿什么样的清单?我想过要使用一个并发列表 的CopyOnWriteArrayList,但我也发现了 EventListenerList对象 存在。 Java中监听器列表的最佳实践方法是什么?


4331
2018-04-12 10:55


起源

EventListenerList似乎是一个很好的候选人 - Sanjeev
我用了一个 List<ExpansionListener> 首先,如果你只想允许那种倾听者。然后我可能会去 CopyOnWriteArrayList 因为对侦听器的更改可能很少发生,但写入可能由多个线程并行发生(尽管只是对您的架构进行了假设)。 - Thomas
一个 Set 更适合防止对象拥有多个相同类型的侦听器。 - Titus
@Sanjeev:我查看了源代码 EventListenerList,它似乎不是线程安全的(寻找可能不是一件坏事)。 @Thomas:另外,不是线程安全的。 @Titus:是的,a Set 摆脱多个听众注册听起来并不坏。是否有一些特定于侦听器的内置类型,或仅使用默认的并发类型? - Markus Weninger
List<ExpansionListener> 没有说明线程安全, CopyOnWriteArrayList 意味着是线程安全的(JavaDoc说它是“ArrayList的线程安全变体”)。 - Thomas


答案:


CopyOnWriteArrayList 是线程安全的。并非所有Swing组件都是线程安全的。

注意:在Java 8之前,迭代这个集合会产生垃圾,但是在Java 8中 Iterator 可以使用Escape Analysis放置在堆栈中。

final List<EventListener> listeners = new CopyOnWriteArrayList<>();

if (!listeners.contains(listener))
     listeners.add(listener);

使用Set是优选的esp,因为您可能希望以线程安全的方式忽略侦听器的重复注册。

final Set<EventListener> listeners = new CopyOnWriteArraySet<>();

listeners.add(listener);

尽管如此,表现将大致相同 CopyOnWriteArraySet 有这样的优势 add 是原子的,在做什么 contains 接着 add 不是原子的。

编辑:正如@Hulk建议你可以使用CopyOnWriteArrayList.addIfAbsent,但是这个方法在List上不可用。

final CopyOnWriteArrayList<EventListener> listeners = new CopyOnWriteArrayList<>();

listeners.addIfAbsent(listener);

10
2018-04-12 11:10



@Hulk就是为了 CopyOnWriteArrayList 虽然它不可用 List - Peter Lawrey