在我的用例中,我想更新变量的值,并在流的下一次迭代中引用它。
但是java编译器给了我错误。这是我的代码
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));
}
我试图将列表中的值变为字符串,直到长度[直到此迭代]&lt; = maxlength
有人可以帮我解决这个问题吗?我收到Local variables referenced from a lambda expression must be final or effectively final
错误。
答案 0 :(得分:2)
你的变量必须是最终的/有效的最终才能在lambda中使用它。您仍然可以通过使用最终的容器对象来实现目标。就像一个数组 - 特别是你的例子,long[1]
或AtomicLong
可以正常工作 - 引用是最终的,但你可以更改内容。
基于您的代码的示例:
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));
注意,您还可以按如下方式简化过滤器:
.filter(p->{
long pLength = p.getBytes(StandardCharsets.UTF_8).length;
currentLength[0] = currentLength[0] + pLength;
return (currentLength[0] + maxLength <= maxLength);
})
答案 1 :(得分:1)
一旦满足条件,您应该使用循环和break;
。它更快,因为它可以提前纾困(流将遍历整个列表)并且不违反Stream.filter的规范,这要求传递的谓词必须无状态。
Stream<T> filter(Predicate<? super T> predicate)
谓词 - 应用于每个元素的非干扰无状态谓词,以确定是否应包含它
答案 2 :(得分:1)
我不确定Stream
在执行有状态操作时是否是正确的工具,但无论如何这里的解决方案有效,但看起来有些 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));
}