99import org .jetbrains .annotations .Nullable ;
1010
1111import java .util .ArrayList ;
12- import java .util .Arrays ;
1312import java .util .Collections ;
1413import java .util .List ;
1514import java .util .concurrent .Semaphore ;
1615import java .util .concurrent .atomic .AtomicReference ;
1716
18-
1917public class ListenerList {
2018 private static final List <ListenerList > allLists = new ArrayList <>();
2119 private static int maxSize = 0 ;
@@ -98,38 +96,45 @@ public static synchronized void unregisterAll(int id, IEventListener listener) {
9896 }
9997
10098 private static class ListenerListInst {
99+ // Enum#values() performs a defensive copy for each call.
100+ // As we never modify the returned values array in this class, we can safely reuse it.
101+ private static final EventPriority [] EVENT_PRIORITY_VALUES = EventPriority .values ();
102+
101103 private boolean rebuild = true ;
102104 private AtomicReference <IEventListener []> listeners = new AtomicReference <>();
103- private final ArrayList <ArrayList <IEventListener >> priorities ;
105+
106+ /** A lazy-loaded array of lists containing listeners for each priority level. */
107+ @ SuppressWarnings ("unchecked" )
108+ private final @ Nullable ArrayList <IEventListener >[] priorities =
109+ (ArrayList <IEventListener >[]) new ArrayList [EVENT_PRIORITY_VALUES .length ];
110+
104111 private ListenerListInst parent ;
105112 private List <ListenerListInst > children ;
106113 private final Semaphore writeLock = new Semaphore (1 , true );
107114
108- private ListenerListInst () {
109- int count = EventPriority .values ().length ;
110- priorities = new ArrayList <>(count );
115+ private ListenerListInst () {}
111116
112- for (int x = 0 ; x < count ; x ++)
113- priorities .add (new ArrayList <>());
117+ private ListenerListInst (ListenerListInst parent ) {
118+ this .parent = parent ;
119+ this .parent .addChild (this );
114120 }
115121
116122 public void dispose () {
117123 writeLock .acquireUninterruptibly ();
118- priorities .forEach (ArrayList ::clear );
119- priorities .clear ();
124+ for (int i = 0 ; i < priorities .length ; i ++) {
125+ @ Nullable ArrayList <IEventListener > priority = priorities [i ];
126+ if (priority != null ) {
127+ priority .clear ();
128+ priorities [i ] = null ;
129+ }
130+ }
120131 writeLock .release ();
121132 parent = null ;
122133 listeners = null ;
123134 if (children != null )
124135 children .clear ();
125136 }
126137
127- private ListenerListInst (ListenerListInst parent ) {
128- this ();
129- this .parent = parent ;
130- this .parent .addChild (this );
131- }
132-
133138 /**
134139 * Returns a ArrayList containing all listeners for this event,
135140 * and all parent events for the specified priority.
@@ -141,7 +146,7 @@ private ListenerListInst(ListenerListInst parent) {
141146 */
142147 public ArrayList <IEventListener > getListeners (EventPriority priority ) {
143148 writeLock .acquireUninterruptibly ();
144- ArrayList <IEventListener > ret = new ArrayList <>(priorities . get (priority . ordinal () ));
149+ ArrayList <IEventListener > ret = new ArrayList <>(getListenersForPriority (priority ));
145150 writeLock .release ();
146151 if (parent != null )
147152 ret .addAll (parent .getListeners (priority ));
@@ -187,33 +192,45 @@ private void addChild(ListenerListInst child) {
187192 * Rebuild the local Array of listeners, returns early if there is no work to do.
188193 */
189194 private void buildCache () {
190- if (parent != null && parent .shouldRebuild ())
195+ if (parent != null && parent .shouldRebuild ())
191196 parent .buildCache ();
192197
193198 ArrayList <IEventListener > ret = new ArrayList <>();
194- Arrays . stream (EventPriority . values ()). forEach ( value -> {
199+ for (EventPriority value : EVENT_PRIORITY_VALUES ) {
195200 List <IEventListener > listeners = getListeners (value );
196- if (listeners .size () > 0 ) {
197- ret .add (value ); //Add the priority to notify the event of it's current phase.
198- ret .addAll (listeners );
199- }
200- });
201+ if (listeners .isEmpty ()) continue ;
202+ ret .add (value ); // Add the priority to notify the event of its current phase.
203+ ret .addAll (listeners );
204+ }
201205 this .listeners .set (ret .toArray (new IEventListener [0 ]));
202206 rebuild = false ;
203207 }
204208
205209 public void register (EventPriority priority , IEventListener listener ) {
206210 if (listener == null ) return ;
207211 writeLock .acquireUninterruptibly ();
208- priorities . get (priority . ordinal () ).add (listener );
212+ getListenersForPriority (priority ).add (listener );
209213 writeLock .release ();
210214 this .forceRebuild ();
211215 }
212216
213217 public void unregister (IEventListener listener ) {
214218 writeLock .acquireUninterruptibly ();
215- priorities .stream ().filter (list -> list .remove (listener )).forEach (list -> this .forceRebuild ());
219+ boolean needsRebuild = false ;
220+ for (var list : priorities ) {
221+ if (list == null ) continue ;
222+ needsRebuild |= list .remove (listener );
223+ }
224+ if (needsRebuild ) this .forceRebuild ();
216225 writeLock .release ();
217226 }
227+
228+ private ArrayList <IEventListener > getListenersForPriority (EventPriority priority ) {
229+ var listenersForPriority = priorities [priority .ordinal ()];
230+ if (listenersForPriority == null )
231+ listenersForPriority = priorities [priority .ordinal ()] = new ArrayList <>();
232+
233+ return listenersForPriority ;
234+ }
218235 }
219236}
0 commit comments