There are a lot of mistakes I’ve seen people make when coding event listeners (and more commonly the classes that trigger events) in Java. One of the most common mistakes is:
List listeners = new ArrayList();
So whats the problem here? Well for one thing: ArrayList (like it says in the JavaDoc) is not synchronized, so it’s great for a single-thread environment. However, GUI’s are very often multi-threaded environments (unless you do everything on the EventDispatchThread, which is also a bad idea), so you may end up with a corrupt list of listeners.
Synchronization and threading issues are not massively likely in a GUI environment, but GUI applications are not the only ones with events, the problem can be solved in a few simple ways:
List listeners = new Vector();
or a more modern approach:
List listeners = Collections.synchronizedList(new ArrayList());
There is also the lovely javax.swing.EventListenerList which also happens to be a much more memory efficient solution. Bare in mind that it’s not bound to Swing in any way (other than the package it’s in). ArrayList and Vector both allocate memory for 10 entires when they are first initialized, whats more they grow exponentially in size. This is great if you are working with rapidly growing lists, but event listener lists tend to grow slowly and very seldom. EventListenerList lazy-creates it’s internal list, and so pre-allocates no “real” memory (space for a pointer, thats about it).
My preference for lists of event listeners is the java.util.concurrent.CopyOnWriteArrayList:
List<MouseListener> listeners = new CopyOnWriteArrayList<MouseListener>();
The CopyOnWriteArrayList is expensive to write to (add or remove), but wonderfully memory efficient, and inherently thread-safe. So unlike other List types, when firing an event off, you don’t have to synchronize on the List object, since if a write happens at the same time, the iterator you use points to a different array to the edited one.
MouseEvent e = new MouseEvent(...);
for(MouseListener l : listeners) {
l.mousePressed(e);
}
vs.
MouseEvent e = new MouseEvent(...):
synchronized(listeners) {
for(MouseListener l : listeners) {
l.mousePressed(e);
}
}
One last note: When dealing with PropertyChangeListeners and Events (which is probably 60% of all events I work with), be sure to use a java.beans.PropertyChangeSupport to handle things for you.
August 30, 2007 at 10:56 am
good article..
wud like to visit again…
________
“a newbie in tecnology….
August 31, 2007 at 7:59 am
Your point about “javax.swing.EventListenerList… [is] not bound to Swing in any way other than the package it’s in” brings to mind a whole huge misconception about Jini in the early days (after we got past the “it’s about devices” crap.) Lots of people thought (still think) that its tied to RMI, simply because key interfaces defined in the Jini Specification extend java.rmi.Remote, and declare that (most) methods might throw RemoteException.
Well, duh!? All they’re doing is declaring the semantics that “we may be far away in the network — expect failures” (See the “Eight Fallacies”) rather than (as most programmers seem to assume) “we’re tied to RMI’s mechanisms and are stuck with using RMI”.
“Good News, Everyone!”…