繁体   English   中英

ConcurrentHashMap.keySet()。removeAll()性能问题

[英]ConcurrentHashMap.keySet().removeAll() performance issue

我正在阅读有关ConcurrentHashMap并检查了其键集的removeAll()实现。 在当前实现中,即使给定集合仅包含一个元素或不包含任何元素,JAVA也会迭代整个键集数据结构。

实际执行

public final boolean removeAll(Collection<?> c) { 
        if (c == null) throw new NullPointerException(); 
        boolean modified = false; 
        for (Iterator<E> it = iterator(); it.hasNext();) { 
            if (c.contains(it.next())) { 
                it.remove(); 
                modified = true; 
            } 
        } 
        return modified; 
    } 

有人可以告诉我这是否是JAVA开发人员想要的,或者我只是在考虑这个问题

发布预期的实现 ,但坦率地说,我不是以下代码的实际作者,因此在阅读并发哈希图时在某个编码博客上发现了这一点,因此希望与其他人共享。

public boolean removeAll(Collection<?> c) { 
        boolean modified = false; 

        if (size() > c.size()) { 
            for (Iterator<?> i = c.iterator(); i.hasNext(); ) 
                modified |= remove(i.next()); 
        } else { 
            for (Iterator<?> i = iterator(); i.hasNext(); ) { 
                if (c.contains(i.next())) { 
                    i.remove(); 
                    modified = true; 
                } 
            } 
        } 
        return modified; 
    }

常规Map (例如HashMap )中的KeySet实现AbstractSet 对于OpenJDK 8,源代码将其显示为removeAll方法:

public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    boolean modified = false;

    if (size() > c.size()) {
        for (Iterator<?> i = c.iterator(); i.hasNext(); )
            modified |= remove(i.next());
    } else {
        for (Iterator<?> i = iterator(); i.hasNext(); ) {
            if (c.contains(i.next())) {
                i.remove();
                modified = true;
            }
        }
    }
    return modified;
}

如您所见,将检查哪个集合具有最多的条目。 如果Set本身具有较大的大小,则在给定的collection参数上进行迭代。 否则,它会在Set本身的条目上完成。 因此,如果您传递的集合没有条目,则将执行零次实际循环执行。 如果Set本身没有条目,则相同。

但是,对于ConcurrentHashMapkeySet()方法将返回内部类KeySetView的实例,该实例实现了另一个内部类CollectionView 该代码的removeAll实现确实符合您发布的代码,该代码始终在KeySetView条目本身而不是给定的集合上进行迭代。

其原因很可能是视图(键集或条目集)返回的Iterator通过反映在请求Iterator时存在的值而允许并发访问。 ConcurrentHashMap的Javadoc中:

类似地,迭代器和枚举返回在创建迭代器/枚举时或此后某个时刻反映哈希表状态的元素。 他们不抛出ConcurrentModificationException。 但是,迭代器被设计为一次只能由一个线程使用。

因此,该方法强制使用键视图的Iterator本身来照顾视图或地图上并发操作之间的一致性。

但是请注意,上述AbstractSet实现也不一定是最佳的。 如果作为参数提供的集合c大小大于集合的大小, c.contains(element)element中的每个element调用c.contains(element) ,但取决于contains方法的集合的类型,效率可能不及该集合。 例如,对于ArrayListcontains线性运行时间,而对象在集合中的存在将在恒定时间内检测到。

集合与集合

有问题的实现来自AbstractCollection类,而解决方案的实现来自AbstractSet ,后者继承自AbstractCollection

性能的提高源于以下事实:在Collection您不能保证元素是唯一的,因此size()调用不足以优化移除。 但是,在Set中,保证了唯一性,因此可以进行其他假设并提高性能,例如在确定要删除的元素时遍历较小的集合。

高度专业化的地图及其集合

但是,在ConcurrentHashMap 集和集上,故事完全不同。 这是因为ConcurrentHashMap是高度专业的Map具有许多假设和对并发性的改进。 这使得Set.removeAll()此类常规性能改进不再有效(根据实际实现)。

ConcurrentHashMap迭代器是弱一致性的,在幕后做了很多魔术。 也许密钥集上的removeAll是人们为所有其他性能(从并发访问的角度来看)所付出的代价。

只要按照iterator().remove()ConcurrentHashMap下来的兔孔ConcurrentHashMap.replaceNode()看到多少逻辑中hinding replaceNode()的源代码 ,以适应去除迭代器元件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM