突变流中的元素

Ada*_*ord 7 java java-8 java-stream

是否有一个"最佳实践"来改变流中的元素?我特指的是流管道中的元素,而不是它之外的元素.

例如,考虑我想获取用户列表,为null属性设置默认值并将其打印到控制台的情况.

假设User类:

class User {
    String name;

    static User next(int i) {
        User u = new User();
        if (i % 3 != 0) {
            u.name = "user " + i;
        }
        return u;
    }
}
Run Code Online (Sandbox Code Playgroud)

在java 7中,它有点像:

for (int i = 0; i < 7; i++) {
    User user = User.next(i);
    if(user.name == null) {
        user.name = "defaultName";
    }
    System.out.println(user.name);
}
Run Code Online (Sandbox Code Playgroud)

在java 8中,我似乎使用.map()并返回对mutated对象的引用:

IntStream.range(0, 7)
    .mapToObj(User::next)
    .map(user -> {
        if (user.name == null) {
            user.name = "defaultName";
        }
        return user;
    })
    //other non-terminal operations
    //before a terminal such as .forEach or .collect
    .forEach(it -> System.out.println(it.name));
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来实现这一目标?也许使用.filter()来处理null变异然后连接未过滤的流和过滤的流?有些巧妙使用Optional?目标是在终端.forEach()之前使用其他非终端操作的能力.

在流的"精神"中,我试图在没有中间集合和简单的"纯粹"操作的情况下执行此操作,这些操作不依赖于管道外的副作用.

编辑:官方流java文档声明'少量流操作,如forEach()和peek(),只能通过副作用操作; 这些都应该小心使用.鉴于这将是一个不干扰的操作,具体是什么使它危险?我见过的例子到达管道之外,这显然是粗略的.

ass*_*ias 9

不要改变对象,直接映射到名称:

IntStream.range(0, 7)
    .mapToObj(User::next)
    .map(user -> user.name)
    .map(name -> name == null ? "defaultName" : name)
    .forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)


Lou*_*man 5

听起来您正在寻找peek

.peek(user -> {
    if (user.name == null) {
        user.name = "defaultName";
    }
})
Run Code Online (Sandbox Code Playgroud)

...虽然不清楚您的操作实际上是否需要修改流元素,而不是仅仅通过您想要的字段:

.map(user -> (user.name == null) ? "defaultName" : user.name)
Run Code Online (Sandbox Code Playgroud)

  • 来自`peek` javadoc:“_此方法的存在主要是为了支持调试,您希望在其中查看元素流经管道中的某个点时_” (7认同)
  • @DidierL:嗯,是的,那是因为你实际上不应该修改流管道中的元素。不过,如果你想的话,就是这样。 (2认同)