0

I have a problem with lambda expressions in java.

List<String> l = new ArrayList<String>();
l.add("Besit");
l.add("Java");
String str = "Java";
boolean flag = false;
int counter = 0;
l.forEach((h) -> {
    if (h.equals(str)) {
        counter++;
        flag = true.
    }
});

Obviously, I have error, because forEach doesn't see str and flag, I know that in C++ lambdas we can pass some parameters by reference. Is it possible to do this in java?

7
  • h = "Change" does not change the value in the list. It just changes what local variable h refers to. If you want to change the value in the list, you must use a ListIterator, so you can't use Streams for that. If you must use Streams, use map(...) and build a new list. Commented Jun 6, 2019 at 23:00
  • My mistake, I was trying to add something Commented Jun 6, 2019 at 23:02
  • No, Java doesn't have by-reference parameters. You'll have to use a mutable wrapper if you want to modify an object by reference. Or just iterate over the indexes and call set(). Commented Jun 6, 2019 at 23:03
  • Ok thanks now I understand Commented Jun 6, 2019 at 23:04
  • @shmosel "iterate over the indexes and call set()" Would be better to use a ListIterator and its set() method, since that'll perform much better if the list is e.g. a LinkedList. Commented Jun 6, 2019 at 23:05

3 Answers 3

4

You should approach the problem differently, in order that you don't need to update flag in the lambda (you can access str though):

boolean flag = l.stream().anyMatch(h -> h.equals(str));

For your updated code, you do this with:

int counter = (int) list.stream().filter(h -> h.equals(str)).count();
// or int counter = (int) list.stream().filter(h -> h.equals(str)).mapToInt(h -> 1).sum(); 
boolean flag = count > 0;
Sign up to request clarification or add additional context in comments.

2 Comments

And if I really want lambda to change something in my list?
It's unclear what you mean, because you're not even trying to change something in the list in your example.
0

It's not that str, counter and flag are not seen, it's that they need to be effectively final, in other words, the code of the lambda can't change them, which would be true in any case because they are passed by value and String is not modifiable. You could create another object with public fields or getter/setter methods for the various quantities you're interested in, declare an instance of the object as final, and pass it in...

public class Stuff {

   public String str = "Java";
   public int counter = 0;
   public boolean flag = false;
}

final Stuff stuff = new Stuff();

l.forEach((h) -> {
    if (h.equals(stuff.str)) {
        stuff.counter++;
        stuff.flag = true.
    }
});

Comments

0

@AndyTurner's answer is correct, however I want to explain why you can't do what you where trying.

A lambda can only see variables in the containing scope if they are marked as final (or are effectively final).

Your String str is effectively final, but you can't mark boolean flag as final, because you want your lambda to modify it.

One way to get around this is to use a final AtomicBoolean instead of your boolean flag. You can then call set() on the AtomicBoolean to change the value it contains.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.