【发布时间】:2017-03-13 11:04:19
【问题描述】:
我有字符串流和像
这样的空值Stream<String> str1 = Stream.of("A","B","C",null,null,"D",null,"E","F",null,"G",null);
我想将它简化为另一个流,其中任何非空字符串序列连接在一起,即喜欢
Stream<String> str2 = Stream.of("ABC", "", "D", "EF","G")
我发现的第一种方法 - 创建收集器,首先将完整的输入流减少为具有所有连接字符串列表的单个对象,然后从中创建新流:
class Acc1 {
final private List<String> data = new ArrayList<>();
final private StringBuilder sb = new StringBuilder();
private void accept(final String s) {
if (s != null)
sb.append(s);
else {
data.add(sb.toString());
sb.setLength(0);
}
}
public static Collector<String,Acc1,Stream<String>> collector() {
return Collector.of(Acc1::new, Acc1::accept, (a,b)-> a, acc -> acc.data.stream());
}
}
...
Stream<String> str2 = str.collect(Acc1.collector());
但在这种情况下,在任何使用 str2 之前,即使是 str2.findFirst(),输入流也会被完全处理。它消耗时间和内存的操作以及来自某个生成器的无限流它根本无法工作
另一种方式 - 创建将保持中间状态并在 flatMap() 中使用的外部对象:
class Acc2 {
final private StringBuilder sb = new StringBuilder();
Stream<String> accept(final String s) {
if (s != null) {
sb.append(s);
return Stream.empty();
} else {
final String result = sb.toString();
sb.setLength(0);
return Stream.of(result);
}
}
}
...
Acc2 acc = new Acc2();
Stream<String> str2 = str1.flatMap(acc::accept);
在这种情况下,从 str1 将只检索真正通过 str2 访问的元素。
但是使用在流处理之外创建的外部对象对我来说看起来很难看,并且可能会导致一些我现在看不到的副作用。此外,如果 str2 稍后将与 parallelStream() 一起使用,则会导致不可预测的结果。
有没有更正确的流->流减少没有这些缺陷的实现?
【问题讨论】:
-
有一个库“StreamEx”,它包含 grouptun 方法(不记得确切的名称)。使用它,您可以将您的流转换为填充和空列表的流。下一步是显而易见的。
-
@Josep Prat:您应该永远将
reduce与修改其参数的函数一起使用。 OP 已经使用的collect方法是可变归约的正确方法。事实上,无论您使用reduce还是collect,减少都会处理所有元素,不会改变。 -
@Josep Prat:您从一个空的
List作为“身份函数”开始,并省略了该列表如何与累加器函数中的字符串一起播放,但显然已经考虑修改List,您还写了“组合器是列表连接 (addAll)”,如果不是修改输入列表之一的函数,List.addAll是什么? -
好吧,你的评论不应该是一个完整的解决方案,但是你为什么建议使用
reduce呢?与基于Collector的解决方案相比,OP 已经在问题中发布的所谓优势是什么? -
@Josep Prat:当然,当你只有不可变的数据结构时,没有必要区分。这也意味着该语言的库将提供处理这些结构的必要工具(例如返回新列表的列表连接,但可能会在运行时以某种方式在后台进行优化,以减少甚至消除复制开销)。但在这里,问题不在于如何减少……
标签: java java-8 java-stream