在 Stream 的 collect() 终端操作中,如果 supplier 是一个像 String 这样的不可变对象会怎样?
In collect() terminal operation of Stream, what will happen if supplier is an immutable object like a String?
Stream 的 collect()
方法是一个可变的缩减。基于 Java 文档:
A mutable reduction operation accumulates input elements into a mutable result container, such as a Collection or StringBuilder, as it processes the elements in the stream.
我尝试了以下方法,编译没有问题。
Stream<String> stream1 = Stream.of("w", "o", "l", "f");
String word = stream1.collect(String::new, String::concat, String::concat);
System.out.println(word);
如果供应商是 StringBuffer,我将收集操作视为元素将附加到提供的 StringBuffer。
既然 String 是一个不可变对象,这里的可变缩减是如何工作的?会不会和reduce操作一样,每次实现累加器都会创建一个新对象?
根据 Oracle / Java 文档:
收集
<R> R collect(Supplier<R> supplier, <R,? super T> accumulator, <R,R> combiner)
对此流的元素执行可变缩减操作。可变缩减是这样一种缩减,其中缩减值是可变结果容器,例如 ArrayList,并且通过更新结果状态而不是替换结果来合并元素。
可变减少
可变缩减操作在处理流中的元素时将输入元素累积到可变结果容器中,例如 Collection 或 StringBuilder。
如果我们想获取字符串流并将它们连接成一个长字符串,我们可以通过普通归约来实现:
String concatenated = strings.reduce("", String::concat)
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#MutableReduction
所以,简而言之,它之所以有效,是因为在幕后 java 使用了 stringbuilder
How does mutable reduction works here since String is an immutable object?
没有。当您 运行 时,您将得到一个空字符串(Supplier only
的结果)。编译器无法强制检查 Supplier return 是否为不可变对象,这是它绝对做不到的事情。由于您的容器是不可变的,因此对它的更新将被忽略。就像做:
String s = "abc";
s.concat("def"); // the result is ignored here
如果你把它写成 lambda 表达式,可能会更有意义:
Stream<String> stream1 = Stream.of("w", "o", "l", "f");
String word = stream1.collect(
String::new,
(left, right) -> {
left.concat(right); // result is ignored
},
String::concat);
另一方面,当您使用 reduce 时,您 被迫 到 return 某些东西:
String word = stream1.reduce(
"",
(x, y) -> {
return x.concat(y);
},
(x, y) -> {
return x.concat(y);
});
当然,你还可以这样做:
String word = stream1.reduce(
"",
(x, y) -> {
x.concat(y);
return x; // kind of stupid, but you could
},
(x, y) -> {
return x.concat(y);
});
如果你想打破它;但这不是重点。
collect()
方法是一个可变的缩减。基于 Java 文档:
A mutable reduction operation accumulates input elements into a mutable result container, such as a Collection or StringBuilder, as it processes the elements in the stream.
我尝试了以下方法,编译没有问题。
Stream<String> stream1 = Stream.of("w", "o", "l", "f");
String word = stream1.collect(String::new, String::concat, String::concat);
System.out.println(word);
如果供应商是 StringBuffer,我将收集操作视为元素将附加到提供的 StringBuffer。
既然 String 是一个不可变对象,这里的可变缩减是如何工作的?会不会和reduce操作一样,每次实现累加器都会创建一个新对象?
根据 Oracle / Java 文档:
收集
<R> R collect(Supplier<R> supplier, <R,? super T> accumulator, <R,R> combiner)
对此流的元素执行可变缩减操作。可变缩减是这样一种缩减,其中缩减值是可变结果容器,例如 ArrayList,并且通过更新结果状态而不是替换结果来合并元素。
可变减少
可变缩减操作在处理流中的元素时将输入元素累积到可变结果容器中,例如 Collection 或 StringBuilder。 如果我们想获取字符串流并将它们连接成一个长字符串,我们可以通过普通归约来实现:
String concatenated = strings.reduce("", String::concat)
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#MutableReduction
所以,简而言之,它之所以有效,是因为在幕后 java 使用了 stringbuilder
How does mutable reduction works here since String is an immutable object?
没有。当您 运行 时,您将得到一个空字符串(Supplier only
的结果)。编译器无法强制检查 Supplier return 是否为不可变对象,这是它绝对做不到的事情。由于您的容器是不可变的,因此对它的更新将被忽略。就像做:
String s = "abc";
s.concat("def"); // the result is ignored here
如果你把它写成 lambda 表达式,可能会更有意义:
Stream<String> stream1 = Stream.of("w", "o", "l", "f");
String word = stream1.collect(
String::new,
(left, right) -> {
left.concat(right); // result is ignored
},
String::concat);
另一方面,当您使用 reduce 时,您 被迫 到 return 某些东西:
String word = stream1.reduce(
"",
(x, y) -> {
return x.concat(y);
},
(x, y) -> {
return x.concat(y);
});
当然,你还可以这样做:
String word = stream1.reduce(
"",
(x, y) -> {
x.concat(y);
return x; // kind of stupid, but you could
},
(x, y) -> {
return x.concat(y);
});
如果你想打破它;但这不是重点。