Java 8流获取无法在原始类型void上调用map

use*_*190 4 java lambda java-8 java-stream

我正在玩Java 8中的流和lambdas,因为我之前从未使用它们并试图转换20到19的所有人的年龄并打印出他们的名字,但是我得到以下错误

Cannot invoke map(Person::getName) on the primitive type void
Run Code Online (Sandbox Code Playgroud)

这是我的代码

            System.out.println(
                people.stream()
                .filter(person -> person.getAge() == 20)
                .forEach(person -> person.setAge(19))
                .map(Person::getName)); 
Run Code Online (Sandbox Code Playgroud)

如果有人能告诉我为什么会这样,或者让我知道如何改进或修改此代码,我们将不胜感激.

Aar*_*ron 8

forEach 是一个终端操作,不会返回它工作的流.

通常,您应该避免使用forEach修改流,即使是最终操作.相反,您应该使用map修改流的项目.

以下是如何执行此操作的示例,其中包括合法使用forEach:

people.stream()
      .filter(person -> person.getAge() == 20)
      .map(person -> new Person(person.getName(), person.getAge() -1 /*, ...*/))
      .forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

关于该代码的一些注意事项:

  • map(...)只转换流,而不是数据源(the people Collection).如果以后要使用转换后的结果,你会想.collect()Stream进入一个新的Collection与适当的Collector(例如Collectors.toList())

  • 如果您遵循这条道路,那么您的新终端操作就是collect(),并且您不能再使用.forEach()了.一种解决方案是使用.forEach()新方法Collection(不需要a stream,从1.8开始forEach()实现Iterable),另一种方法是.peek()在流上使用你map()作为非终端等效的.forEach()

  • 关于使用peek(),请注意非终端操作是由终端操作驱动的:如果这个只返回流的一个元素(如.findFirst()),则只对该元素执行非终端操作,你不应该'除此之外.

  • 只要你能证明同一个`Person`实例在流中永远不会两次,在一个动作中调用`setAge`就没有干扰(否则,你正在修改`filter`访问的相同属性).不过,你应该考虑["......真的只是为了调试吗?"](http://stackoverflow.com/q/33635717/2711488) (3认同)
  • @ user6248190这不是一回事:不是将原来的`Person`流映射到年龄少一年的`Person`流,而是将流映射到`String`流,这是'Person`的名称.还是一样的年龄 (2认同)
  • 我相信peek不应该干扰:也就是说,修改元素很好,但修改流本身不是. (2认同)
  • 这很有道理,感谢您的反馈!我仍然不完全相信,但我会确保进一步研究这一点. (2认同)
  • 好的.我想我发现peek最大的问题是:只有当一些终端操作在它之后才会被调用.没有forEach之后,它就不会被调用.我用forEach示例更新了我的答案,而无需创建新的Person. (2认同)