我想用Java 8编写纯函数,它将集合作为参数,对该集合的每个对象应用一些更改,并在更新后返回一个新集合。我想遵循FP原则,所以我不想更新/修改作为参数传递的集合。
有没有办法使用Stream API而不先创建原始集合的副本(然后使用forEach或'normal'进行循环)?
下面的示例对象,假设我想将文本附加到其中一个对象属性:
public class SampleDTO {
private String text;
}
所以我想做类似下面的事情,但不修改集合。假设“list”是List<SampleDTO>
。
list.forEach(s -> {
s.setText(s.getText()+"xxx");
});
答案 0 :(得分: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());
:
{{1}}
答案 1 :(得分:2)
为了使这种方式更加优雅,我建议在课堂上创建一个Method
。
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;
}
}
并添加如下:
List<SampleDTO> output =list.stream().map(SampleDTO::getSampleDTO).collect(Collectors.toList();
答案 2 :(得分:2)
我认为更简洁、更易读的解决方案是:
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());
或者,如果您有 SampleDTO 的复制构造函数,您可以将 map
函数替换为 .map(SampleDTO::new)
。
peek
函数将对 Stream 的元素提供操作,而不会更改 Stream 的结果类型。
答案 3 :(得分:1)
Streams像字符串一样是不可变的,所以你无法绕过需要创建新的流/列表/数组
话虽如此,您可以使用.Collect()返回更改后的新集合
/etc/hosts
答案 4 :(得分:1)
我认为,如果进行多线程工作,将原始列表流式传输到新的修改列表或其他任何需要的内容会更好。
新的列表或地图或您想要的任何其他结构都可以作为流媒体处理的一部分创建。
完成流媒体处理后,只需将原始内容与新内容交换即可。
所有这些都应该发生在同步块中。
通过这种方式,您可以获得reduce的最大性能和并行性,无论您正在做什么,并完成原子交换。