I've stream of strings and nulls like
Stream<String> str1 = Stream.of("A","B","C",null,null,"D",null,"E","F",null,"G",null);
I want to reduce it to another stream, where any sequence of not null string joined together, ie like
Stream<String> str2 = Stream.of("ABC", "", "D", "EF","G")
First way, that i found - create collector that firstly reduce complete input stream to single object with list of all joined strings and then create new stream from it:
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());
But in this case before any use if str2, even as str2.findFirst(), input stream will be completely processed. It time and memory consuming operation and on infinity stream from some generator it will not work at all
Another way - create external object that will keep intermediate state and use it in 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);
In this case from str1 will be retrieved only elemets that really accessed via str2.
But using of external object, created outside of stream processing, looks ugly for me and probably can cause some side effects, that i do not see now. Also if str2 will be used later with parallelStream() it will cause unpredictable result.
Is there any more correct implemetation of stream->stream reduction without these flaws?