0

I'm iterating list of string and checking if string contains colon :, if it does then split it and assign result in variables. Following code works but how to write it effectively using Java 8 lambda expression.

String key = null;
String value = null;

List<String> testList = new ArrayList<>();
testList.add("value1");
testList.add("value2");
testList.add("value3");
testList.add("value4");
testList.add("value5:end");

for(String str : testList) {
  if(str.contains(":" )) {
    String[] pair = str.split(":");
    key = pair[0];
    value = pair[1];
  }  
}

Tried the equivalent of the above code in Java 8, however I'm getting an error that Local variable in an enclosing scope must be final or effectively final.

testList.stream().forEach(str -> {
  if(str.contains(":" )) {
     String[] pair = str.split(":");
    key = pair[0];
    value = pair[1];
  }  
});
Vijay Nandwana
  • 2,476
  • 4
  • 25
  • 42
  • 6
    What do you *want* to happen if there are multiple strings containing a colon? Your working code will only take any notice of the final one, which doesn't seem ideal. – Jon Skeet Jan 20 '17 at 08:38
  • 1
    the problem is that `key` and `value` is not `final`. – ymonad Jan 20 '17 at 08:39
  • 3
    Lambdas are very similar to anonymous classes so try your first snipped using an anonymous implementation of the interface being passed and you should get the same error. Then read up on the scoping of variables when anonymous classes are involved, e.g. here: http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class – Thomas Jan 20 '17 at 08:39
  • 2
    Also think about what would happen if you'd use a parallel stream and you'd have more than one string matching the condition: what should the values of `key` and `value` be? – Thomas Jan 20 '17 at 08:41
  • 1
    If you want to use a stream, you should *use* the stream API, instead of trying to have just a different syntax for your loop. → `Optional o = testList.stream() .filter(str -> str.contains(":")) .map(str -> str.split(":")) .findFirst();` Whether this is an improvement for this trivial example, is a different question… – Holger Jan 20 '17 at 11:33

1 Answers1

1

Maybe you should consider this approach:

    String key = null;
    String value = null;

    List<String> testList = new ArrayList<>();
    testList.add("value1");
    testList.add("value2");
    testList.add("value3");
    testList.add("value4");
    testList.add("value5:end");

    Optional<String> optString = testList.stream().map(str -> {
        if (str.contains(":")) return str;
        else return null;
            })
            .filter(str -> str != null)
            .findAny();

        if(optString.isPresent()){
        String []pair = optString.get().split(":");
        key = pair[0];
        value = pair[1];

    }
GypsumLady
  • 81
  • 3
  • 2
    There is no sense in conditionally mapping to `null`, just to filter out `null` values afterwards. Why not just filter based on the condition in the first place? → `.filter(str -> str.contains(":"))` – Holger Jan 20 '17 at 11:49