In my use case I would like to update the value of a variable and reference the same in next iteration in streams.
But the java compiler is giving me error. Here is my code
static String convertList(
List<Map.Entry<String, String>> map,
String delimiter,
long maxLength
) {
long currentLength = 0L;
return map.stream()
.map(e->e.getKey() + "=" + e.getValue())
.filter(p->{
long pLength = p.getBytes(StandardCharsets.UTF_8).length;
currentLength = currentLength + pLength;
if (currentLength <= maxLength) {
return true;
} else {
return false;
}
})
.collect(Collectors.joining(delimiter));
}
I am trying to get the values from the list to a string until the length [till this iteration] <= maxlength
could someone help me fix this? I am getting Local variables referenced from a lambda expression must be final or effectively final
error.
Your variable must be final / effectively final to use it inside lambda. You can still accomplish your goal by using a final 'container object' like an array - specifically for your example, long[1]
or AtomicLong
would work well - the reference is final, but you can change out the contents.
Example based on your code:
final long[] currentLength = new long[1];
return map.stream()
.map(e->e.getKey() + "=" + e.getValue())
.filter(p->{
long pLength = p.getBytes(StandardCharsets.UTF_8).length;
currentLength[0] = currentLength[0] + pLength;
if (currentLength[0] + maxLength <= maxLength) {
return true;
} else {
return false;
}
}).collect(Collectors.joining(delimiter));
Note, you can also simplify your filter as follows:
.filter(p->{
long pLength = p.getBytes(StandardCharsets.UTF_8).length;
currentLength[0] = currentLength[0] + pLength;
return (currentLength[0] + maxLength <= maxLength);
})
You should use a loop and break;
once your condition is fulfilled. It is faster because it can bail out early (the stream would traverse the whole list) and does not violate the specification of Stream.filter which requires that the passed predicate must be stateless.
Stream<T> filter(Predicate<? super T> predicate)
predicate - a non-interfering, stateless predicate to apply to each element to determine if it should be included
I'm not sure that Stream
is the right tool when doing stateful operations but anyway here is a solution that works but looks a bit hacky.
private static String convertList(List<Map.Entry<String, String>> map, String delimiter, long maxLength) {
AtomicLong currentLength = new AtomicLong();
return map.stream()
.map(e -> e.getKey() + "=" + e.getValue())
.peek(s -> currentLength.addAndGet(s.getBytes(StandardCharsets.UTF_8).length))
.filter(p -> currentLength.get() <= maxLength)
.collect(Collectors.joining(delimiter));
}