Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么';事件列表是否已被替换?(或者:替换它的陷阱是什么?)_Java_Swing_Event Handling - Fatal编程技术网

Java 为什么';事件列表是否已被替换?(或者:替换它的陷阱是什么?)

Java 为什么';事件列表是否已被替换?(或者:替换它的陷阱是什么?),java,swing,event-handling,Java,Swing,Event Handling,我们的传统应用程序使用了一个糟糕的框架(好吧,我来点名吧,它是Tapestry 4),其中包含了大量的事件监听器(~100000),用于最简单的操作。我猜这超出了javax.swing.event.EventListenerList要处理的范围,在这个不幸的用例中,它给我们带来了一些令人头痛的性能问题 我花了几个小时准备了下面基于HashMap/ArrayList的相当简单的替换,它几乎在各个方面都要快得多: 添加50000个侦听器: EventListenerList>2秒 EventLis

我们的传统应用程序使用了一个糟糕的框架(好吧,我来点名吧,它是Tapestry 4),其中包含了大量的
事件监听器(~100000),用于最简单的操作。我猜这超出了javax.swing.event.EventListenerList
要处理的范围,在这个不幸的用例中,它给我们带来了一些令人头痛的性能问题

我花了几个小时准备了下面基于HashMap/ArrayList的相当简单的替换,它几乎在各个方面都要快得多:

添加50000个侦听器:

  • EventListenerList
    >2秒
  • EventListenerMap
    约3.5毫秒
向50000名听众发送火灾事件:

  • EventListenerList
    0.3-0.5毫秒
  • EventListenerMap
    0.4-0.5毫秒
删除50000个侦听器(一次一个):

  • EventListenerList
    >2秒
  • EventListenerMap
    约280毫秒
发射可能只是稍微慢一点,但修改速度要快得多。诚然,这个框架给我们带来的情况是病态的,但它似乎仍然可以在很久以前被取代。显然,公共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 );
    }
}
公共类EventListenerMap
{
private final ReadWriteLock=new ReentrantReadWriteLock();
私有最终锁readLock=Lock.readLock();
private final Lock writeLock=Lock.writeLock();
私有映射llMap=newhashmap();
公共void添加(类listenerClass,L listener)
{
尝试
{
writeLock.lock();
List List=getListenerList(listenerClass);
if(list==null)
{
列表=新的ArrayList();
llMap.put(listenerClass,list);
}
添加(侦听器);
}
最后
{
writeLock.unlock();
}
}
公共无效删除(类listenerClass,L listener)
{
尝试
{
writeLock.lock();
List List=getListenerList(listenerClass);
如果(列表!=null)
{
删除(侦听器);
}
}
最后
{
writeLock.unlock();
}
}
@抑制警告(“未选中”)
公共L[]getListeners(类listenerClass)
{
L[]copy=(L[])Array.newInstance(listenerClass,0);
尝试
{
readLock.lock();
List List=getListenerList(listenerClass);
如果(列表!=null)
{
复制=(L[])列表。到阵列(复制);
}
}
最后
{
readLock.unlock();
}
返回副本;
}
@抑制警告(“未选中”)
私有列表getListenerList(类listenerClass)
{
return(List)llMap.get(listenerClass);
}
}

这是一个优化问题。Swing的EventListenerList假定:

  • 列表上的侦听器数量非常少
  • 监听器列表的数量可能非常大
  • 添加/删除事件很少发生
考虑到这些假设,添加和删除项目的计算成本可以忽略不计,但让这些列表闲置的内存成本可能会很大。这就是为什么
EventListenerList
通过分配一个足够大的数组来容纳侦听器,从而使内存占用尽可能小。(,在添加第一个侦听器之前,它甚至不会分配任何内容,以确保没有侦听器时不会浪费空间。)其缺点是,每次添加新元素时,它都会重新分配数组并复制所有旧元素,当侦听器太多时,会给您带来天文数字的成本

(实际上,它的内存效率并没有尽可能高;列表是{Type,Listener}对,因此如果它是数组数组的数组,在某些情况下会稍微小一些。)

至于您的解决方案:
HashMap
s过度分配内存以确保高效的哈希。同样,默认的
ArrayList
构造函数为10个元素分配空间,并以块的形式增长。在一个奇怪的代码库中,每个列表上都有100k个侦听器,这个额外的内存只是用来保存所有侦听器的内存的一个小小的增加

但是,在较轻的侦听器负载下,您的实现需要为每个空列表花费16个指针(默认分配为
HashMap
),为带有一个元素的
EventListenerMap
花费26个指针,为带有两个不同类元素的映射花费36个指针。(这不包括
HashMap
ArrayList
结构大小的其余部分。)对于相同的情况,
EventListenerList
分别花费0、2和4个指针


这似乎是对您的代码的巨大改进。

+1本月的疯狂(iest)问题,Q about(对于最简单的操作)每个步骤都是独立的还是链接的,从树或信号量开始,如果是,那么另一个疯狂(iest)建议,使用EventHandler并在运行时以字符串形式构建路径,然后可能不需要在内存中保存一堆方法,phaaa我喜欢这个想法,来自你的问题,让RPG永远存在(然后是相同的疯狂无休止的代码),你在相对触发时间中有输入错误吗?您报告说您的实现可能会“慢一点”,0.4毫秒,而
EventListenerList
则是0.3秒