I am trying to write a code to find min and max in an array when I write code using basic java I can use normal variable but when I use forEach loop complier forces me use atomic varaible.Why is it necessary to use atomic variable inside a forEach loop I have attached a sample code for reference
class SingleScan{
public void elementsData(int []data){
int min=data[0];
int max=data[1];
for(int i=0;i<data.length-1;i++){
if(data[i]<min){
min=data[I];//Here I am using normal variable
}else if(data[i]>max){
max=data[i];//Here I am using normal variable
}
}
AtomicInteger min = new AtomicInteger(data[0]);
AtomicInteger max = new AtomicInteger(data[1]);
Arrays.stream(data).forEach(i-> {
if (i< min.get()) {
min.set(i);//Here I have to use atomic variable why I can't simply use normal variables
}
if(i>max.get()){
max.set(i);
}
} );
System.out.println("Max element is "+max +" minimum element is "+min);
}
}
public class MaxMinSingleScan {
public static void main(String[] args) {
int [] element=new int[]{5,8,3,9,6,2,10,7,-1,4};
SingleScan singleScan=new SingleScan();
singleScan.elementsData(element);
}
}
In the code you provided, you need to use
AtomicIntegerinstead of standard variables (int) inside theforEachloop because of the concept of capturing variables in lambda expressions in Java.In Java, when you use a lambda expression or an anonymous inner class, you can only access variables that are effectively final or declared as final. This means you can't modify the value of a regular variable inside the lambda expression or anonymous inner class.
Lambda expressions are designed to allow the use of variables from the enclosing scope, even if they are not explicitly passed as parameters. However, the Java language specification imposes some restrictions on such variables to prevent potential concurrency and mutability issues.
By requiring the variable to be effectively final or explicitly final, the Java compiler ensures that it is not modified after the lambda expression captures it. This guarantees that the lambda operates on a stable value and avoids any unexpected behavior that could arise from concurrent modifications.
An effectively final variable is not explicitly declared as final but is never reassigned after its initial assignment. It behaves as a constant within the scope of the lambda expression.
In your code, you're using a lambda expression with
Arrays.stream(data).forEach(i -> { ... }). Inside this lambda expression, you want to update the values of min and max, but you can't do that since they are regular variables.To work around this limitation, you can use
AtomicInteger, which provides a way to update the value within the lambda expression using the set() method.AtomicIntegeris mutable so that you can modify its value even inside a lambda expression or anonymous inner class.You can also work around this by using an array to store the min and max values and declare them as
final int[] minandfinal int[] max.By using an
array, you effectively create a container to hold a mutable reference to an int value. Even though the array itself is final, the content of the array (i.e., the int value inside) can be modified.Another way can be to create a wrapper around
minandmaxThe choice of whether to use AtomicInteger, an array (int[]), or a mutable wrapper class depends on your specific needs and the context of your code. Here's a summary to help you decide:
If you require thread safety or need to handle concurrent modifications, use AtomicInteger. It provides atomic operations and synchronization guarantees, making it suitable for multi-threaded scenarios.
If you're working in a single-threaded environment and concurrency is not a concern, you can use either an array (int[]) or a mutable wrapper class. Both options allow you to update values within a lambda expression.
Consider the trade-offs between simplicity, performance, and thread safety in your specific scenario. Choose the approach that best aligns with your requirements and ensures the correctness and maintainability of your code.