0

I am using Java 8 stream Iteration with a variable that should be used in other classes also. So I have used the below code.

AtomicBoolean bool = new AtomicBoolean(true);
public void testBool(){
list.stream().forEach(c->{ 
      if( c.getName() != null){
    bool.set(true);
    }
});

}

public void test(){
  if(bool.get()){
    System.out.println("value is there");
  }
}

But I heard like using the Atomic Object will be a performance hit sometimes. Is there any alternate approach to use the variables outside the forEach block with Java 8 usage? Without this am getting the error as a variable should be a final or effectively final error.

Please help me to resolve this issue.

Thanks in advance.

Neela
  • 107
  • 2
  • 10
  • all the code that you write inside a lambda function must be `functional` what that means is : any code that is executing inside the lambda function should not have an impact anywhere else. Your code has that impact on the variable `bool` this is why it's preventing it from doing the same. Please use a different logic like : `list.stream().any( c -> c.getName != null )` – tsamridh86 Aug 18 '21 at 03:48
  • Please post a [https://stackoverflow.com/help/minimal-reproducible-example](https://stackoverflow.com/help/minimal-reproducible-example) I can't reproduce your problem. – CausingUnderflowsEverywhere Aug 18 '21 at 04:06
  • The three most upvoted answers of the [duplicate link](https://stackoverflow.com/questions/34865383/variable-used-in-lambda-expression-should-be-final-or-effectively-final) should provide you with enough information. – MC Emperor Aug 18 '21 at 14:40
  • The "performance" impact of using `AtomicBoolean` is so tiny it would be nearly impossible to measure. However, the performance impact of using a stream that iterates over every element when it doesn't need to, instead of using a simple loop that exits early when the first `getName != null` is found is quite large and easily measurable even with very modestly size lists. – Bohemian Aug 18 '21 at 22:52
  • The *effectively final* restriction only applies to local variables. Because you are setting an instance field, you can simply use: `boolean bool = true;` and set it from within the lambda. – Bohemian Aug 18 '21 at 23:12
  • @Bohemian your comment is very informative and would be awesome as an answer. I converted my answer into community wiki and would be glad if you insert this valuable information at the start of the answer or allow me to incorporate it. – CausingUnderflowsEverywhere Aug 20 '21 at 18:22
  • @CausingUnderflowsEverywhere use my comments as you like. – Bohemian Aug 20 '21 at 18:29
  • I have used a list with fixed size and it is working fine List list = new ArrayList<>(1); list.add(true); list.get(0); – Neela Aug 31 '21 at 02:01

2 Answers2

0

You could avoid the problem by using the lambda expression to return true or false if there are any names that are not null, and assign the result to your boolean.

Something like this:

boolean hasAnyWithNames = list.stream().anyMatch(c -> c.getName() != null);

The choice bool is not a good one for variable name by the way.

Edit:

  • Replaced Boolean with base type per comment.
  • Used anyMatch() instead of filter() count per comment Thanks
TuGordoBello
  • 4,350
  • 9
  • 52
  • 78
Omeri
  • 187
  • 2
  • 16
  • 2
    Use `anyMatch` and further, don’t use `Boolean` objects without a reason. Just `boolean`. “hasEmptyNames” is a strange choice for “there are any names that are not null”. – Holger Aug 18 '21 at 07:53
  • Good point guys! – Omeri Aug 18 '21 at 14:07
-1

The effectively final restriction only applies to local variables.
Since you are setting an instance field, you can simply use: boolean bool = true; and set it from within the lambda.

If you need to use a local variable, add the final modifier to the declaration.

Regarding the overhead of using AtomicBoolean, keep in mind that streams also have overhead and if you're only using them for iteration you're better off using a for loop instead:

boolean bool = true;

    public void testBool(){
        for (var c : list) {
            if (c.getName() != null) {
                bool = true;
                break;
            }
        }
    }

Lastly, as @Neela mentioned in a comment, a more efficient use of streams would be with the following code*:

boolean bool = list.stream().anyMatch(c -> c.getName() != null);

*Note, the original code has an error that results in bool always being true and this is avoided by not presetting true and directly putting the result of anyMatch.

  • Setting `bool` to the result of `anyMatch()` is *not* the same effect as OP's code: If `bool` is already `true`, but none the the elements have a non-null name, OP's code would leave `bool` as `true`, but your code would set it to `false`. Although OP's code may not reflect OP's intention, your code nevertheless may produce a different result. – Bohemian Aug 18 '21 at 23:07