Наше устаревшее приложение застряло в ужасной структуре (хорошо, я назову имена, это Tapestry 4), что связано с нелепым числом EventListeners
(~ 100 000) для самых простых операций. Я предполагаю, что это выходит за рамки того, что javax.swing.event.EventListenerList
когда-либо предназначалось для обработки, и в этом неудачном случае использования это вызывает у нас некоторые неприятные головные боли производительности.
Я провел пару часов, подбирая довольно наивную замену на основе HashMap/ArrayList
ниже, и она значительно быстрее почти всеми способами:
Добавить 50 000 слушателей:
-
EventListenerList
> 2 секунды -
EventListenerMap
~ 3,5 миллисекунды
Событие пожара до 50 000 слушателей:
-
EventListenerList
0,3-0,5 миллисекунды -
EventListenerMap
0,4-0,5 миллисекунды
Удалите 50 000 слушателей (по одному):
-
EventListenerList
> 2 секунды -
EventListenerMap
~ 280 миллисекунд
Увольнение может быть только медленнее, но модификация значительно быстрее. По общему признанию, ситуация, с которой эта структура вложила нас в себя, является патологическим, но по-прежнему кажется, что EventListenerList
можно было заменить давно. Очевидно, что есть проблемы с публичным API (например, он раскрывает свой внутренний внутренний массив состояний), но для этого должно быть больше. Может быть, есть многопоточные случаи, когда EventListenerList
намного безопаснее или более результативен?
public class EventListenerMap
{
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private Map<Class, List> llMap = new HashMap<Class, List>();
public <L extends EventListener> void add ( Class<L> listenerClass, L listener )
{
try
{
writeLock.lock();
List<L> list = getListenerList( listenerClass );
if ( list == null )
{
list = new ArrayList<L>();
llMap.put( listenerClass, list );
}
list.add( listener );
}
finally
{
writeLock.unlock();
}
}
public <L extends EventListener> void remove ( Class<L> listenerClass, L listener )
{
try
{
writeLock.lock();
List<L> list = getListenerList( listenerClass );
if ( list != null )
{
list.remove( listener );
}
}
finally
{
writeLock.unlock();
}
}
@SuppressWarnings("unchecked")
public <L extends EventListener> L[] getListeners ( Class<L> listenerClass )
{
L[] copy = (L[]) Array.newInstance( listenerClass, 0 );
try
{
readLock.lock();
List<L> list = getListenerList( listenerClass );
if ( list != null )
{
copy = (L[]) list.toArray( copy );
}
}
finally
{
readLock.unlock();
}
return copy;
}
@SuppressWarnings("unchecked")
private <L extends EventListener> List<L> getListenerList ( Class<L> listenerClass )
{
return (List<L>) llMap.get( listenerClass );
}
}