如何在Java8流/过滤器中使用非final变量?

时间:2016-10-21 01:45:59

标签: java java-8 java-stream

在我的用例中,我想更新变量的值,并在流的下一次迭代中引用它。

但是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错误。

3 个答案:

答案 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));
}