[英]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
本身没有条目,则相同。
但是,对于ConcurrentHashMap
, keySet()
方法将返回内部类KeySetView
的实例,该实例实现了另一个内部类CollectionView
。 该代码的removeAll
实现确实符合您发布的代码,该代码始终在KeySetView
条目本身而不是给定的集合上进行迭代。
其原因很可能是视图(键集或条目集)返回的Iterator
通过反映在请求Iterator时存在的值而允许并发访问。 从ConcurrentHashMap
的Javadoc中:
类似地,迭代器和枚举返回在创建迭代器/枚举时或此后某个时刻反映哈希表状态的元素。 他们不抛出ConcurrentModificationException。 但是,迭代器被设计为一次只能由一个线程使用。
因此,该方法强制使用键视图的Iterator
本身来照顾视图或地图上并发操作之间的一致性。
但是请注意,上述AbstractSet
实现也不一定是最佳的。 如果作为参数提供的集合c
大小大于集合的大小, c.contains(element)
为element
中的每个element
调用c.contains(element)
,但取决于contains
方法的集合的类型,效率可能不及该集合。 例如,对于ArrayList
, contains
线性运行时间,而对象在集合中的存在将在恒定时间内检测到。
有问题的实现来自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.