{{announcement.body}}
{{announcement.title}}

Modifying Variables Inside Lambdas

DZone 's Guide to

Modifying Variables Inside Lambdas

In this article, we discuss how to modify variables inside of Lambda expressions in a pinch with Java's AtomicInteger and arrays.

· Java Zone ·
Free Resource

Occasionally, we will run into a situation in which we want to modify a variable inside a Lambda expression, but when we try to do so, we get a compile-time error saying: 

A variable inside Lambda must be Final or Effective Final.

Let's take a sample problem where we would require a variable to be mutated inside a Lambda expression.

Romzo works at FedEx. FedEx uses advance containers of variable size (First item size + 6), depending upon the first item inserted into it. Suppose we inserted the first item of weight 5 then container can take items till weight 5 + 6, i.e 11.

We are given items  1, 2, 3, 21, 7, 12, 14, 21, and we need to find the minimum number of containers required to ship our consignment.

Solution:
There are items with weights { 1, 2, 3, 21, 7, 12, 14, 21 }. This can be broken into 3 containers: { 1,2,3,7 }, { 12,14 }, and { 21,21 }.

Each container will contain items weighing within units of the minimum weight item+6.

Explanation

The first container holds items weighing 1,2,3 and 7 (weights till 7 ,i.e 1+6)
The second container holds the items weighing 12,14 units. (weights till 18 ,i.e 12+6 =18)
The third container holds the item weighing  21,21 units. (weights till 27 ,i.e 21+6 =27)

You may also like: Lambda Expressions in Java 8.

One solution without Lambda : 

Java




xxxxxxxxxx
1
15


 
1
 public static void main(String[] args) {
2
int [] items = {1, 2, 3, 21, 7, 12, 14, 21};
3
int count = 1;
4
Arrays.sort(items); //Sort the items ascending by weight
5
int currentWeight = items[0];
6
for(int weight : items)
7
{
8
if(!(weight <= currentWeight+6))
9
{
10
count++;
11
currentWeight = weight;
12
}
13
}
14
System.out.println(count);
15
 }



Now, when we try to solve the above problem with lambda and stream, then we worry that updating the count variable inside our lambda.

But, we cant update the field which is declared final:

Java




xxxxxxxxxx
1
15


 
1
public static void main(String[] args) {
2
final int count = 0; //variable inside lambda must be final or effective final
3
final int t = 0; //variable inside lambda must be final or effective final
4
int[] A = {1, 2, 3, 21, 7, 12, 14, 21};
5
int k = 6;
6
Arrays.sort(A);
7
IntStream.rangeClosed(0, A.length - 1).boxed().map(x -> {
8
if (! (A[t] + k >= A[x])) {
9
count++; //cannot do this as the count variable is final
10
t = x; //cannot do this as the count variable is final
11
}
12
return count;
13
}).collect(Collectors.toList());
14
System.out.println(count + 1);
15
 }



The above problem arises due to Java 8 Language Specification, §15.27.2 which says :

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression
must either be declared final or be effectively final (§4.12.4),
or a compile-time error occurs where the use is attempted.

To overcome this, we can come up with 3 potential solutions:

1. Making the variable static.
2. Use Array
3. Atomic Integer

1. When we make the variable  count  and  t   static , then some huge problem of conflicting read/write will start if we are working in a multi-threaded environment.

But, when you are sure that there are no other threads except the one you are currently working, then you are free to use this method. But the use of static variable must be avoided.

Java
 




xxxxxxxxxx
1
15


1
static int count = 0;
2
static int t = 0;
3
public static void main(String[] args) {
4
int[] A = {1, 2, 3, 21, 7, 12, 14, 21};
5
int k = 6;
6
Arrays.sort(A);
7
IntStream.rangeClosed(0, A.length - 1).boxed().map(x -> {
8
if (! (A[t] + k >= A[x])) {
9
count++;
10
t = x;
11
}
12
return count;
13
}).collect(Collectors.toList());
14
System.out.println(count + 1);
15
}



2. Using array  instead of  static variables. This leverages the benefit of array data structure keeping in mind that only the external variable inside lamdba must be final; here we are kind of tricking the lambda.

Java




xxxxxxxxxx
1
15


1
public static void main(String[] args) {
2
final int [] count = {0}; //final 
3
final int [] t = {0}; //final
4
int[] A = {1, 2, 3, 21, 7, 12, 14, 21};
5
int k = 6;
6
Arrays.sort(A);
7
IntStream.rangeClosed(0, A.length - 1).boxed().map(x -> {
8
if (! (A[t[0]] + k >= A[x])) {
9
count[0]=count[0]+1; // works, as we trick the lambda keeping the external variable as final
10
t[0] = x; //works, as we trick the lambda keeping the external variable as final 
11
}
12
return count;
13
}).collect(Collectors.toList());
14
System.out.println(count[0] + 1);
15
 }



3. Using  AtomicInteger . This makes sure that the int value is updated atomically, thus making the code thread-safe. It has inbuilt methods like getAndIncrement(), set(), addAndGet(), etc., which can be performed atomically.  

Java




xxxxxxxxxx
1
15


1
public static void main(String[] args) {
2
int[] A = {1, 2, 3, 21, 7, 12, 14, 21};
3
int k = 6;
4
AtomicInteger count = new AtomicInteger(0); //Effective Final
5
final AtomicInteger t = new AtomicInteger(0); //Final
6
Arrays.sort(A);
7
IntStream.rangeClosed(0, A.length - 1).boxed().map(x -> {
8
if (! (A[t.intValue()] + k >= A[x])) {
9
count.incrementAndGet();
10
t.set(x);
11
}
12
return count;
13
}).collect(Collectors.toList());
14
System.out.println(count.incrementAndGet());
15
}



Difference between Final and Effective Final:

A variable is final or effectively final when it's initialized once and is never mutated in its owner class; we can't initialize it in loops or inner classes.

Final:

Java




xxxxxxxxxx
1


1
final int x;
2
x = 3;



Effectively Final:

Java




xxxxxxxxxx
1


1
int x;
2
x = 4; //value is never changed ever, so kind of make=ing it effectively final.
3
 
          
4
A keyword before a variable makes it final and no final keyword before a variable meke it effectively final provided we never change its value.



Further Reading

Topics:
java-8 ,java ,stream api ,lambda ,atomic integer

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}