Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to increment a value in Java Stream?

I want to increment value of index with the each iteration by 1. Easily to be achieved in the for-loop. The variable image is an array of ImageView.

Here is my for-loop.

for (Map.Entry<String, Item> entry : map.entrySet()) {      
    image[index].setImage(entry.getValue().getImage());
    index++;
}

In order to practise Stream, I have tried to rewrite it to the Stream:

map.entrySet().stream()
    .forEach(e -> item[index++].setImage(e.getValue().getImage()));

Causing me the error:

error: local variables referenced from a lambda expression must be final or effectively final

How to rewrite the Stream incrementing the variable index to be used in?

like image 780
Nikolas Charalambidis Avatar asked Oct 28 '16 12:10

Nikolas Charalambidis


2 Answers

You shouldn't. These two look similar, but they are conceptually different. The loop is just a loop, but a forEach instructs the library to perform the action on each element, without specifying neither the order of actions (for parallel streams) nor threads which will execute them. If you use forEachOrdered, then there are still no guarantees about threads, but at least you have the guarantee of happens-before relationship between actions on subsequent elements.

Note especially that the docs say:

For any given element, the action may be performed at whatever time and in whatever thread the library chooses. If the action accesses shared state, it is responsible for providing the required synchronization.

As @Marko noted in the comments below, though, it only applies to parallel streams, even if the wording is a bit confusing. Nevertheless, using a loop means that you don't even have to worry about all this complicated stuff!

So the bottom line is: use loops if that logic is a part of the function it's in, and use forEach if you just want to tell Java to “do this and that” to elements of the stream.

That was about forEach vs loops. Now on the topic of why the variable needs to be final in the first place, and why you can do that to class fields and array elements. It's because, like it says, Java has the limitation that anonymous classes and lambdas can't access a local variable unless it never changes. Meaning not only they can't change it themselves, but you can't change it outside them as well. But that only applies to local variables, which is why it works for everything else like class fields or array elements.

The reason for this limitation, I think, is lifetime issues. A local variable exists only while the block containing it is executing. Everything else exists while there are references to it, thanks to garbage collection. And that everything else includes lambdas and anonymous classes too, so if they could modify local variables which have different lifetime, that could lead to problems similar to dangling references in C++. So Java took the easy way out: it simply copies the local variable at the time the lambda / anonymous class is created. But that would lead to confusion if you could change that variable (because the copy wouldn't change, and since the copy is invisible it would be very confusing). So Java just forbids any changes to such variables, and that's that.

There are many questions on the final variables and anonymous classes discussed already, like this one.

like image 159
Sergei Tachenov Avatar answered Nov 09 '22 15:11

Sergei Tachenov


Some kind of "zip" operation would be helpful here, though standard Stream API lacks it. Some third-party libraries extending Stream API provide it, including my free StreamEx library:

IntStreamEx.ints() // get stream of numbers 0, 1, 2, ...
           .boxed() // box them
           .zipWith(StreamEx.ofValues(map)) // zip with map values
           .forKeyValue((index, item) -> image[index].setImage(item.getImage()));

See zipWith documentation for more details. Note that your map should have meaningful order (like LinkedHashMap), otherwise this would be pretty useless...

like image 37
Tagir Valeev Avatar answered Nov 09 '22 15:11

Tagir Valeev