Java 8修改流元素

Zyg*_*yga 14 java java-8 java-stream

我想用Java 8编写纯函数,将集合作为参数,对该集合的每个对象应用一些更改,并在更新后返回一个新集合.我想遵循FP原则,所以我不想更新/修改作为参数传递的集合.

有没有办法使用Stream API而不先创建原始集合的副本(然后使用forEach或'normal'for循环)?

下面的示例对象,并假设我想将文本追加到其中一个对象属性:

public class SampleDTO {
    private String text;
}
Run Code Online (Sandbox Code Playgroud)

所以我想做类似下面的事情,但不修改集合.假设"列表"是一个List<SampleDTO>.

list.forEach(s -> {
    s.setText(s.getText()+"xxx");
});
Run Code Online (Sandbox Code Playgroud)

Era*_*ran 23

您必须有一些方法/构造函数来生成现有SampleDTO实例的副本,例如复制构造函数.

然后你可以将map每个原始SampleDTO实例添加到一个新SampleDTO实例,并将collect它们变成一个新实例List:

List<SampleDTO> output = 
    list.stream()
        .map(s-> {
                     SampleDTO n = new SampleDTO(s); // create new instance
                     n.setText(n.getText()+"xxx"); // mutate its state
                     return n; // return mutated instance
                 })
       .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)


soo*_*man 5

为了使这种方式更加优雅,我建议Method在类中创建一个 with 。

 public class SampleDTO {
 private String text;
public String getText() {
    return text;
}

public void setText(String text) {
    this.text = text;
}

public SampleDTO(String text) {
    this.text = text;
}

public SampleDTO getSampleDTO() {
    this.setText(getText()+"xxx");
    return this;
}
    }
Run Code Online (Sandbox Code Playgroud)

并将其添加为:

List<SampleDTO> output =list.stream().map(SampleDTO::getSampleDTO).collect(Collectors.toList();
Run Code Online (Sandbox Code Playgroud)

  • getSampleDTO 方法修改其文本?方法的不同命名和非常令人讨厌的副作用 (5认同)

Ale*_*lex 5

我认为更干净、更易读的解决方案是:

List<SampleDTO> unmodifiable = Arrays.asList(new SampleDTO("1"), new SampleDTO("2"));

List<SampleDTO> modified = unmodifiable.stream()
            .map(s -> new SampleDTO(s.getText())) // '.map(SampleDTO::new)' with copy constructor
            .peek(s -> s.setText(s.getText() + "xxx"))
            .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

或者,如果您有 SampleDTO 的复制构造函数,您可以将map函数替换为.map(SampleDTO::new).

peek函数将在不更改 Stream 的结果类型的情况下对 Stream 的元素提供操作。

  • 注意:您不应在生产代码中使用 peek()。仅用于调试。 (3认同)
  • @ghost28147这意味着“peek”是惰性方法,不能对流中的所有元素执行,例如当您使用方法“findFirst”时(您可以找到流中的第一个元素,并且“peek”不会对其他元素执行) elements),但如果您在“collect”方法之前使用它,这意味着您的所有元素将用于插入集合中,您可以使用“peek”。一般来说,如果您知道流内部如何工作,您可以使用“peek”方法。文档说“主要是为了支持调试”,而不是“不得在生产中使用”。 (3认同)