Normally I would recommend going by the loop for-each
traditional. It solves much of what you normally want when you go through a collection.
Of course, there are some exceptions. And there are also convention reasons to treat it differently (I’ll get there).
for-each
The most trivial cases of iterating a collection (whatever) is simply treating each element of the collection, without altering the collection itself. And usually you save the result of the evaluation in a variable external to the loop, to work with this result later. For these cases, the greater simplicity of the for-each
is enough.
In addition, you have all the advantages of flow control of a conventional loop:
continue
to avoid unnecessary processing (and extra levels of indentation in blocks if
)
break
to stop the execution of the loop
return
when you already have the desired result and there is no reason to continue processing
Iterator
The tie through the Iterator
is only strictly necessary if you want to change the collection during iteration. Usually, in a Set
, I only saw being used to remove elements from the collection. In a List
I’ve seen being used to replace elements.
Like the noose for-each
uses the Iterator
underneath, but hiding all this code framework in a sweet and fluffy syntax, if you’re not going to use the proper methods of Iterator
, then you’d better use the for-each
.
Even so, even though Iterator
the most suitable for these cases, the team here at work prefers to circumvent its use. So (in the era of Java 7 + Retrolambda, without the right to Streams
of Java 8), to remove elements, we detect all elements that need to be removed and store in a list of actions what we wanted:
Set<Elemento> conjunto = ...;
ArrayList<Runnable> acoesRemocaoPostuma = new ArrayList<>();
for (Elemento e: conjunto) {
if (devoRemover(e)) {
acoesRemocaoPostuma.add(() -> conjunto.remove(e));
}
}
for (Runnable acao: acoesRemocaoPostuma) {
acao.run();
}
In addition, you have all the advantages of flow control of a conventional loop:
continue
to avoid unnecessary processing (and extra levels of indentation in blocks if
)
break
to stop the execution of the loop
return
when you already have the desired result
Iterator.forEachRemaining
I never used and I don’t know totally its use, I am obliged to update this answer in the future.
Method .forEach
Normally I only see practical advantage in using this method in 2 scenarios:
- you get a
Consumer
as a parameter
- you only need to call a function to evaluate the elements
For example, run all actions in a list of Runnable
would look like this:
List<Runnable> listaAcoes = ...;
listaAcoes.forEach(Runnable::run);
If I need, for each element of the set, call a function called sincroniza
passing as parameter a string representing the destination:
String destino = ...;
Set<Elemento> conjunto = ...;
conjunto.forEach(e -> sincroniza(destino, e));
The equivalent via loop of this code is a little more verbose:
String destino = ...;
Set<Elemento> conjunto = ...;
for (Elemento e: conjunto) {
sincroniza(destino, e);
}
In addition, it also has an advantage of convention by iterating like this. In a team formed by young people and without much experience, the use of ties "encourages" change values of local variables, but external to the specific iteration. And, well... in a legacy code that wasn’t properly structured as it grew, it can generate unwanted side effects and hide bugs. Forcing to always use lambda functions, these changes do not occur, are prohibited by the language.
One disadvantage of using this structure to iterate is that it does not support loop control. You cannot directly ignore elements with continue
, or also stop processing with a break
let alone use a return
to warn the caller that he has found what is desired.
using Stream
s to replace ties
Another way to iterate on elements is to use one stream
of Java 8. And this has been seen with very good eyes by my team, but it is not unanimous. Use stream
implies a less imperative/structured and more declarative/functional thinking, and functional programming has both fans (type I) and detractors, being people on the fence rare specimens that need to be studied.
With the construction of stream
, you can simulate much of the flow control that you did with the loop.
Only count strings that start with "marm"? Filter and count:
conjuntoDeStringa.stream().filter(s -> s.startsWith("marm")).count();
Return some Elemento
that is active? Returning null
if you don’t find?
conjunto.stream().filter(Elemento::estahAtivo).findAny().orElse(null);
Concatenate all objects, converting them with toString
and separating them with ;
?
conjunto.stream().map(Object::toString).collect(Collectors.joining(";"));
Find out which of the elements Elemento
returns the largest integer in valorResgate
? Returning -1 in the case of empty set?
conjunto.stream().mapToInt(Elemento::valorResgate).max().orElse(-1);
Here where I work we work a lot with BigDecimal
, and it is very easy for the programmer to make mistakes such as adding up the elements of a list. So we prefer to use stream
to resolve this and avoid that pass the review these bugs:
itens.stream().map(Item::getVrItem).collect(BigDecimalUtils.coletorSoma());
Due to the idiosyncrasies of our system running at the same time in Java, Javascript (transformed from Java by GWT) and Totalcross, we need to use a wrapper of BigDecimal
proper, since Totalcross did not support at the time with fullness java.math.BigDecimal
.
In general, experience here at work indicates that the for-each
is easier, but use streams
proved less problematic and required a higher level of team abstraction, in a way making them think more and better what they want to do.
It depends on a lot of things. Often better a simple
for-each
, but there are cases where you neediterator
. The method.forEach
i don’t see much need to use in the general case, but it gets a more functional look to be. If you receive aConsumer
to make the iteration, then the method.forEach
is the best. I don’t know the.forEachRemaining
– Jefferson Quesado