Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between forEach and forEachEntry in ConcurrentHashMap

Tags:

java

java-8

In Java 8 ConcurrentHashMap had two new methods introduced viz. forEach and forEachEntry.

On closer look both of them have essentially same arguments - forEach has key & value supplied via BiConsumer while forEachEntry has Map.Entry supplied via Consumer from where key & value can be derived.

A simple use case to print all the map entries can be implemented by either of them as below

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()));

Moreover from docs the Map.Entry.setValue isn't supported for bulk operation; so the benefit of having the Map.Entry over plain key-value seems defeated.

.... that may transiently change while computation is in progress; and except for forEach actions, should ideally be side-effect-free. Bulk operations on Map.Entry objects do not support method setValue.

Thus IMO two methods can be used interchangeably (unless I miss something very obvious)

So my questions are

  • Why two methods with essentially same signature were introduced
  • If there are any differences what are they
  • What benefit does one method have over another (a simple use case explaining them would suffice)
like image 469
Bond - Java Bond Avatar asked Mar 15 '17 12:03

Bond - Java Bond


2 Answers

Only difference is that one accepts a BiConsumer and the other just a Consumer.

Here the relevant code:

// 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();
        }
    }
}

Somehow like the two methods to set size of Component: setSize(Dimension) and setSize(int, int)

like image 62
user85421 Avatar answered Oct 18 '22 10:10

user85421


I believe it was just a matter of convenience I for instance prefer to have the key an the value already in the parameters intead of having to call getKey()/getValue() all the time. In fact I don't even use/care with forEach functions, once you become more seasoned in functional programming you will learn this kind of function is by far the least useful of FP, so much that purely functional languages like Haskell doesn't even have them. Everything you expect to do with forEach you can do with map/reduce/collect with the added benefit of no side-effects.

BtW: Posting the link to the Javadoc for convenience. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html

like image 43
Rafael Avatar answered Oct 18 '22 11:10

Rafael