I know that for concurrency reasons I cannot update the value of a local variable in a lambda in Java 8. So this is illegal:
double d = 0;
orders.forEach( (o) -> {
d+= o.getTotal();
});
But, what about updating an instance variable or changing the state of a local object?, For example a Swing application I have a button and a label declared as instance variables, when I click the button I want to hide the label
jButton1.addActionListener(( e) -> {
jLabel.setVisible(false);
});
I get no compiler errors and works fine, but... is it right to change state of an object in a lambda?, Will I have concurrency problems or something bad in the future?
Here another example. Imagine that the following code is in the method doGet of a servlet Will I have some problem here?, If the answer is yes: Why?
String key = request.getParameter("key");
Map<String, String> resultMap = new HashMap<>();
Map<String, String> map = new HashMap<>();
//Load map
map.forEach((k, v) -> {
if (k.equals(key)) {
resultMap.put(k, v);
}
});
response.getWriter().print(resultMap);
What I want to know is: When is it right to mutate the state of an object instance in a lambda?
Yes, you can modify local variables from inside lambdas (in the way shown by the other answers), but you should not do it.
A lambda expression can't define any new scope as an anonymous inner class does, so we can't declare a local variable with the same which is already declared in the enclosing scope of a lambda expression. Inside lambda expression, we can't assign any value to some local variable declared outside the lambda expression.
A Lambda expression can also access an instance variable. Java Developer can change the value of the instance variable even after its defined and the value will be changed inside the lambda as well.
Forcing the variable to be final avoids giving the impression that incrementing start inside the lambda could actually modify the start method parameter.
Your assumptions are incorrect.
You can only change effectively final variables in lambdas, because lambdas are syntactic sugar* over anonymous inner classes.
*They are actually more than only syntactic sugar, but that is not relevant here.
And in anonymous inner classes you can only change effectively final variables, hence the same holds for lambdas.
You can do anything you want with lambdas as long as the compiler allows it, onto the behaviour part now:
Some examples:
class MutableNonSafeInt {
private int i = 0;
public void increase() {
i++;
}
public int get() {
return i;
}
}
MutableNonSafeInt integer = new MutableNonSafeInt();
IntStream.range(0, 1000000)
.forEach(i -> integer.increase());
System.out.println(integer.get());
This will print 1000000 as expected no matter what happens, even though it depends on the previous state.
Now let's parallelize the stream:
MutableNonSafeInt integer = new MutableNonSafeInt();
IntStream.range(0, 1000000)
.parallel()
.forEach(i -> integer.increase());
System.out.println(integer.get());
Now it prints different integers, like 199205, or 249165, because other threads are not always seeing the changes that different threads have made, because there is no synchronization.
But say that we now get rid of our dummy class and use the AtomicInteger
, which is thread-safe, we get the following:
AtomicInteger integer = new AtomicInteger(0);
IntStream.range(0, 1000000)
.parallel()
.forEach(i -> integer.getAndIncrement());
System.out.println(integer.get());
Now it correctly prints 1000000 again.
Synchronization is costly however, and we have lost nearly all benefits of parallelization here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With