Multiple traversals
Howard Lovatt
howard.lovatt at gmail.com
Thu Dec 19 21:15:19 PST 2013
Hi All,
In trying out Streams I keep coming up against an issue where I want two
outputs from a single Stream. I will use the example of finding both min
and max values from a double stream because min and max exist for a stream.
The min and max example implies finite streams, but sometimes you might
want multiple things from an infinite stream.
Various options come to mind:
1. Put everything in an array or List and then use two streams:
double[] values = someStream...process...toArray();
double max =
DoubleStream.of(values).max().orElse(Double.NEGATIVE_INFINITY);
double min =
DoubleStream.of(values).min().orElse(Double.POSITIVE_INFINITY);
Not really very stream like and only works for finite streams.
2. Peek at the first traversal and create a 2nd stream:
DoubleStream.Builder copy = DoubleStream.builder();
double max = someStream...process...peek(d -> copy.add(d))
.max().orElse(Double.NEGATIVE_INFINITY);
double min = copy.build().min().orElse(Double.POSITIVE_INFINITY);
It has got a side effect which is frowned upon and only works for
sequential finite streams.
3. Write a custom collector (for the min/max example a collector already
exists - but it is useful to see what you need to write for when a
collector in't provided):
private static class MinMax {
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
}
private static Collector<Double, MinMax, MinMax> minMax = new
Collector<Double, MinMax, MinMax>() {
@Override
public Supplier<MinMax> supplier() {
return () -> new MinMax();
}
@Override
public BiConsumer<MinMax, Double> accumulator() {
return (accumulator, value) -> {
if (value < accumulator.min) {
accumulator.min = value;
}
else if (value > accumulator.max) {
accumulator.max = value;
}
};
}
@Override
public BinaryOperator<MinMax> combiner() {
return (first, second) -> {
if (first.min < second.min) {
second.min = first.min;
}
if (first.max > second.max) {
second.max = first.max;
}
return second;
};
}
@Override
public Function<MinMax, MinMax> finisher() {
return (result) -> result;
}
@Override
public Set<Collector.Characteristics> characteristics() {
return EnumSet.allOf(Collector.Characteristics.class);
}
};
double[] values = someStream...process...boxed().collect(minMax);
Which is considerably more work and it boxes primitives, but it is the
solution I err towards.
Comments/suggestions? Is there a better approach?
-- Howard.
More information about the lambda-dev
mailing list