forEach/forEachOrdered/forEachRemaining ... iterate?

Peter Levart peter.levart at gmail.com
Thu Jun 13 07:47:36 PDT 2013


I know it's a little late, but let's look at current situation. There 
are 5 methods in current APIs with similar names and signatures + 1 
additional with a little different signature in Map + some similarly 
named methods in ConcurrentHashMap:

interface Stream<T> {
     void forEach(Consumer<? super T> action)
     void forEachOrdered(Consumer<? super T> action)
}

interface Iterable<T> {
     void forEach(Consumer<? super T> action)
}

interface Iterator<T> {
     void forEachRemaining(Consumer<? super T> action)
}

interface Spliterator<T> {
     void forEachRemaining(Consumer<? super T> action)
}

interface Map<K, V> {
     void forEach(BiConsumer<? super K, ? super V> action)
}

class ConcurrentHashMap<K, V> ... {
     void forEachKey(long parallelismThreshold,
                     Consumer<? super K> action)
     <U> void forEachKey(long parallelismThreshold,
                         Function<? super K, ? extends U> transformer,
                         Consumer<? super U> action)
     void forEachValue(long parallelismThreshold,
                       Consumer<? super V> action)
     <U> void forEachValue(long parallelismThreshold,
                           Function<? super V, ? extends U> transformer,
                           Consumer<? super U> action)
}


I'm wondering if the following alternative would be easier to read and 
reason-about in code:

interface Stream<T> {
     void forEach(Consumer<? super T> action)
     void iterate(Consumer<? super T> action)
}

interface Iterable<T> {
     void iterate(Consumer<? super T> action)
}

interface Iterator<T> {
     void iterateRemaining(Consumer<? super T> action)
}

interface Spliterator<T> {
     void iterateRemaining(Consumer<? super T> action)
}

interface Map<K, V> {
     void iterate(BiConsumer<? super K, ? super V> action)
}

class ConcurrentHashMap<K, V> ... {
     void forEachKey(long parallelismThreshold,
                     Consumer<? super K> action)
     <U> void forEachKey(long parallelismThreshold,
                         Function<? super K, ? extends U> transformer,
                         Consumer<? super U> action)
     void forEachValue(long parallelismThreshold,
                       Consumer<? super V> action)
     <U> void forEachValue(long parallelismThreshold,
                           Function<? super V, ? extends U> transformer,
                           Consumer<? super U> action)
}


Why? I'm a little concerned about the duality of Stream.forEach() and 
Iterable.forEach(). While the later is always sequential and 
encounter-ordered, the former can be executed out-of order and/or 
in-parallel. I think that by naming the sequential variants differently, 
the reader of code need not be concerned about the type of the target 
that the method is invoked upon, just the name. This enables fast 
browsing of code where the eye can quickly glance over iterate()s but 
slows down on each forEach()...

How do others feel about the (re)use of forEach... names in different 
APIs? Would it be more difficult to find the right method if iterate() 
was used instead of forEach() for iteration?


Regards, Peter



More information about the lambda-dev mailing list