ConcurrentHashMap 中的 forEach 和 forEachEntry 有什么区别?



在Java 8中,ConcurrentHashMap引入了两种新方法,即:forEachforEachEntry.

仔细观察,它们都有基本相同的参数 -forEach通过BiConsumer提供键和值forEachEntry而通过Consumer提供Map.Entry,从中可以派生键和值。

打印所有映射条目的简单用例可以由它们中的任何一个实现,如下所示

ConcurrentHashMap<String, Integer> map = Stream.of("One", "Two", "Three", "Four", "Five").
collect(Collectors.toConcurrentMap( str -> str, 
str -> str.length(), 
(str, len) -> len, 
ConcurrentHashMap::new));
map.forEach(1, (k, v) -> System.out.println(k + " " + v));
map.forEachEntry(1, entry -> System.out.println(entry.getKey() + " " + entry.getValue()));

此外,从文档中,批量操作不支持Map.Entry.setValue;因此,将Map.Entry置于纯键值之上的好处似乎被打败了。

....在计算过程中可能会瞬时发生变化;并且除每个操作外,理想情况下应该是无副作用的。对Map.Entry对象的批量操作不支持方法setValue

因此,IMO两种方法可以互换使用(除非我错过了一些非常明显的东西)

所以我的问题是

  • 为什么引入两种签名基本相同的方法
  • 如果有任何差异,它们是什么
  • 一种方法比另一种方法有什么好处(一个简单的用例解释它们就足够了)

唯一的区别是,一个接受BiConsumer,另一个只接受消费者。

这里有相关代码:

// forEach
static final class ForEachMappingTask<K,V>
extends BulkTask<K,V,Void> {
final BiConsumer<? super K, ? super V> action;
ForEachMappingTask
(BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
BiConsumer<? super K,? super V> action) {
super(p, b, i, f, t);
this.action = action;
}
public final void compute() {
final BiConsumer<? super K, ? super V> action;
if ((action = this.action) != null) {
for (int i = baseIndex, f, h; batch > 0 &&
(h = ((f = baseLimit) + i) >>> 1) > i;) {
addToPendingCount(1);
new ForEachMappingTask<K,V>
(this, batch >>>= 1, baseLimit = h, f, tab,
action).fork();
}
for (Node<K,V> p; (p = advance()) != null; )
action.accept(p.key, p.val);
propagateCompletion();
}
}
}
// forEachEntry
static final class ForEachEntryTask<K,V>
extends BulkTask<K,V,Void> {
final Consumer<? super Entry<K,V>> action;
ForEachEntryTask
(BulkTask<K,V,?> p, int b, int i, int f, Node<K,V>[] t,
Consumer<? super Entry<K,V>> action) {
super(p, b, i, f, t);
this.action = action;
}
public final void compute() {
final Consumer<? super Entry<K,V>> action;
if ((action = this.action) != null) {
for (int i = baseIndex, f, h; batch > 0 &&
(h = ((f = baseLimit) + i) >>> 1) > i;) {
addToPendingCount(1);
new ForEachEntryTask<K,V>
(this, batch >>>= 1, baseLimit = h, f, tab,
action).fork();
}
for (Node<K,V> p; (p = advance()) != null; )
action.accept(p);
propagateCompletion();
}
}
}

不知何故喜欢设置组件大小的两种方法:setSize(Dimension)setSize(int, int)

我相信这只是一个方便的问题,例如,我更喜欢将键和值已经在参数中,因为必须一直调用getKey()/getValue()。事实上,我甚至不使用/关心forEach函数,一旦你在函数式编程方面变得更加老练,你就会发现这种函数是迄今为止FP中最不有用的,以至于像Haskell这样的纯函数式语言甚至没有它们。您期望用forDone所做的一切都可以通过映射/减少/收集来做,并且没有副作用的额外好处。

顺便说一句:为了方便起见,发布Javadoc的链接。 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html

显然没有功能差异。它只是一个乘法 API,让开发人员感到舒适。通常,您使用 (k,v) 参数进行操作,但有时,您可能会操作入口实例并将它们推送到其他一些使用 Entry<> 对象的 API。 不幸的是,我无法找出任何示例,但是由于Entry是地图的内部类,因此它也可以访问地图的某些成员,该条目存储在其中。

最新更新