I write Engineering Topic weekly...?

Blog of web engineer at Tokyo. [Japanese version] http://taichiw.hatenablog.com/

You should avoid using forEach on Java as much as possible, I think

Collection shouldn't be updated in lambda explanation

Sometimes I see these kind of code.

List<X> list = new ArrayList<>();
/*AnyCollection*/.forEach()
  .map(t -> list.add(t.getXXXX()));

I think it's bad code.

It's not thread safe

If you use parallel stream on above code...

List<X> list = new ArrayList<>();
/*AnyCollection*/.forEach().parallelStream()
  .map(x -> list.add(x.getXXXX()));

it doesn't work correctly because ArrayList's add is not thread safe. "Sometimes" strange null will be added in the list. *1

Of course, you can avoid this trouble if you don't use parallel stream.
However, there is no assuarance that nobody change this part to parallel in the future.

It's not "Function"

Lambda explanations accept only final (or actually final) variable from out of lambda. So any variables doesn't accept new value.*2
On the other hand, "update" isn't restricted like "add to list" or setter.

However, even it's not restricted, we shouldn't do any update against variable, which is defined out of lambda.

Idiom for making collection is existing

This is most common way to make list.

List<X> list = /*AnyCollection*/.stream() //parallel stream can be used
  .map(X::getXXXX)
  .collect(Collector.toList());

This format shows clearly that you want to make list better than previous sample.

Finally, forEach can use for just output

If you keep "rule that you cannot update any external value", only output, like logging, will be the role of forEach.

Then... I think you should avoid using forEach on Java as much as possible.

*1:"Sometimes" is very tough. It's difficult to find this issue by test

*2:It will be compile error