gpt4 book ai didi

java - 如何正确地将流减少到另一个流

转载 作者:搜寻专家 更新时间:2023-11-01 01:25:05 24 4
gpt4 key购买 nike

我有像

这样的字符串和空值流
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());

但是在这种情况下,在使用 if 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() 一起使用,则会导致不可预测的结果。

有没有更正确的 stream->stream reduction 而没有这些缺陷的实现?

最佳答案

Reduction 或其可变变体 collect 始终是一个将处理所有项目的操作。您的操作可以通过自定义实现 Spliterator ,例如

public static Stream<String> joinGroups(Stream<String> s) {
Spliterator<String> sp=s.spliterator();
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<String>(sp.estimateSize(),
sp.characteristics()&Spliterator.ORDERED | Spliterator.NONNULL) {
private StringBuilder sb = new StringBuilder();
private String last;

public boolean tryAdvance(Consumer<? super String> action) {
if(!sp.tryAdvance(str -> last=str))
return false;
while(last!=null) {
sb.append(last);
if(!sp.tryAdvance(str -> last=str)) break;
}
action.accept(sb.toString());
sb=new StringBuilder();
return true;
}
}, false);
}

它会生成预期的组,您可以使用它进行测试

joinGroups(Stream.of("A","B","C",null,null,"D",null,"E","F",null,"G",null))
.forEach(System.out::println);

但也有所需的惰性行为,可通过

测试
joinGroups(
Stream.of("A","B","C",null,null,"D",null,"E","F",null,"G",null)
.peek(str -> System.out.println("consumed "+str))
).skip(1).filter(s->!s.isEmpty()).findFirst().ifPresent(System.out::println);

经过深思熟虑,我想到了这个稍微更高效的变体。只有当至少有两个 String 要加入时,它才会合并 StringBuilder,否则,它只会使用已经存在的唯一 String 实例或空组的文字 "" 字符串:

public static Stream<String> joinGroups(Stream<String> s) {
Spliterator<String> sp=s.spliterator();
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<String>(sp.estimateSize(),
sp.characteristics()&Spliterator.ORDERED | Spliterator.NONNULL) {
private String next;

public boolean tryAdvance(Consumer<? super String> action) {
if(!sp.tryAdvance(str -> next=str))
return false;
String string=next;
if(string==null) string="";
else if(sp.tryAdvance(str -> next=str) && next!=null) {
StringBuilder sb=new StringBuilder().append(string);
do sb.append(next);while(sp.tryAdvance(str -> next=str) && next!=null);
string=sb.toString();
}
action.accept(string);
return true;
}
}, false);
}

关于java - 如何正确地将流减少到另一个流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40340912/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com