how do I copy a java 8 Stream?

Need a function that calculates the min and max from Stream

public static <T> void findMinMax(
            Stream<? extends T> stream,
            Comparator<? super T> order)  

Since Stream allows only one terminal operation to be called, this is not the case:

Optional<? extends T> min =stream.min(order);
Optional<? extends T> max = stream.max(order);  

Is it possible to somehow "split" the streams so that the operation on 1 does not affect the 2nd?

    Object[] mass = new Object[2];

    mass[0] = stream;
    mass[1] = stream;

    Stream<? extends T> str1 = (Stream<? extends T>)mass[0];
    Stream<? extends T> str2 = (Stream<? extends T>)mass[1];

    Optional<? extends T> min =str1.min(order);
    Optional<? extends T> max = str2.max( order);

So, too, the exception flies.


Let's say I made a stream from elements from 1...10

Stream<Integer> str =   Stream.<Integer>iterate(1, n-> n+1)
                              .limit(10);

And the comparator Comparator<Integer> comp = (Integer o1, Integer o2) -> o1.compare(o1, o2);

Passing this to the method:

 public static <T> void findMinMax(
                Stream<? extends T> stream,
                Comparator<? super T> order)

 {

    Object[] mass = new Object[2];
            Stream str = stream;
            mass[0] = null; // max
            mass[1] = null; // min
            if(stream !=null){
                stream.forEach(t-> {
                if(order.compare(t, (T) mass[0])>0) mass[0] = t;
                if(order.compare(t, (T) mass[1])<0) mass[1] = t;
                });
   }
             Optional<? extends T> min =(Optional<? extends T>) Optional.of(mass[0]);
             Optional<? extends T> max = (Optional<? extends T>) Optional.of(mass[1]);

Standard the Integer.compare method is not compared with null and so NullPointerException crashes, I can't initialize arrays with zeros right away, because the passed parameter Stream<? extends T> stream is not only Integer maybe, how to fix this problem?

Author: Tagir Valeev, 2015-11-16

4 answers

public static <T> Pair<T, T> findMinMax(
        Stream<? extends T> stream,
        Comparator<? super T> order, Pair<T, T> identity) {
    return stream.reduce(
            identity, 
            (pair, t) -> {
            T min = order.compare(pair.getKey(), t) < 0 ? pair.getKey() : t;
            T max = order.compare(pair.getValue(), t) > 0 ? pair.getValue() : t;
            return new Pair<>(min, max);
        }, 
            (p1, p2) -> {
            T min = order.compare(p1.getKey(), p2.getKey()) < 0 ? p2.getKey() : p1.getKey();
            T max = order.compare(p1.getValue(), p2.getValue()) > 0 ? p1.getValue() : p1.getValue();
            return new Pair<>(min, max);
        }
    );
}

Some kind of nightmare..

public static void main(String[] args) {
    Pair<Integer, Integer> identity = new Pair<>(Integer.MAX_VALUE, Integer.MIN_VALUE);
    Stream<Integer> s = Stream.of(1,2,3,4,5,6,7,8,9,10);
    Comparator<Integer> comp = (Integer o1, Integer o2) ->  o1.compare(o1, o2);
    Pair<Integer, Integer> p = Example.findMinMax(s, comp, identity);
    System.out.println(p); // 1..10
}

PS: alternative implementation without identity on the advice of @TagirValeev :

public static <T> Pair<T, T> findMinMax(
        Stream<? extends T> stream,
        Comparator<? super T> order) {
    return stream.reduce(
            null,
            (pair, t) -> {
                if (pair == null) return new Pair<>(t,t);
                T min = order.compare(pair.getKey(), t) < 0 ? pair.getKey() : t;
                T max = order.compare(pair.getValue(), t) > 0 ? pair.getValue() : t;
                return new Pair<>(min, max);
        },
            (p1, p2) -> {
                if (p1 == null) return p2;
                if (p2 == null) return p1;
                T min = order.compare(p1.getKey(), p2.getKey()) < 0 ? p2.getKey() : p1.getKey();
                T max = order.compare(p1.getValue(), p2.getValue()) > 0 ? p1.getValue() : p1.getValue();
                return new Pair<>(min, max);}
    );
}
 3
Author: Dmitry V., 2015-11-17 06:02:59

The desire to make two streams from one kills the statement from the interface documentation java.util.Stream:

A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, "forked" streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream.

Apparently, therefore, nothing similar is immediately found on the Internet.

In principle, you can do calculation via Stream.peek, but for this you need to either make a sequential stream, via Stream.sequential, or think about thread safety yourself, something like this:

AtomicReference<String> max = new AtomicReference<String>();
Comparator<String> comparator = Comparator.naturalOrder();

System.out.println( 
        Arrays.stream( new String[] {"one", "two", "абракадабра", "ten", "cat" } )
            .peek( candidate -> max.updateAndGet( current -> {
                if ( current == null ) return candidate;
                return comparator.compare( current, candidate ) >= 0 ? current : candidate;
            } ) )
            .min( comparator ) + " " + Optional.ofNullable( max.get() )
    );
// вывод: Optional[cat] Optional[абракадабра]

For numeric primitives, the library has a java.util.IntSummaryStatistics collector (as well as double and long versions), from which you can get the minimum, maximum, average, and number of elements in the stream. I tried to write my own one the same, but for objects:

public class ObjectSummaryStatistics<T> implements Consumer<T> {
    int count = 0;
    T min = null;
    T max = null;

    final Comparator<T> comparator;

    public static <T> Collector<T, ObjectSummaryStatistics<T>, ObjectSummaryStatistics<T>> collector( final Comparator<T> comparator ) {
        Objects.requireNonNull( comparator, "Must supply comparator." );

        return Collector.<T, ObjectSummaryStatistics<T>, ObjectSummaryStatistics<T>>of(
                () -> new ObjectSummaryStatistics<T>( comparator ),
                ObjectSummaryStatistics::accept, 
                ObjectSummaryStatistics::combine,
                ObjectSummaryStatistics::finish,
                Characteristics.IDENTITY_FINISH
            );
    }


    public ObjectSummaryStatistics( Comparator<T> comparator ) {
        Objects.requireNonNull( comparator, "Must supply comparator." );
        this.comparator = comparator;
    }

    @Override
    public void accept( T value ) {
        Objects.requireNonNull( value, "This collector does not accept nulls" );
        if ( count == 0 ) {
            min = value;
            max = value;
        } else {
            updateMin(value);
            updateMax(value);
        }
        count += 1;
    }

    public ObjectSummaryStatistics<T> combine( ObjectSummaryStatistics<T> other ) {
        if ( other.count > 0 ) {
            if ( this.count == 0 ) {
                this.min = other.min;
                this.max = other.max;
                this.count = other.count;
            } else {
                updateMin( other.min );
                updateMax( other.max );
                this.count += other.count;
            }
        }
        return this;
    }

    public ObjectSummaryStatistics<T> finish() {
        return this;
    }


    private void updateMax(T value) {
        if ( comparator.compare( value, max ) > 0 ) {
            max = value;
        }
    }

    private void updateMin(T value) {
        if ( comparator.compare( value, min ) < 0 ) {
            min = value;
        }
    }

    public int count() {
        return count;
    }

    public Optional<T> max() {
        // если count > 0, у нас должен быть непустой максимум
        return count > 0 ? Optional.of( max ) : Optional.empty();
    }

    public Optional<T> min() {
        return count > 0 ? Optional.of( min ) : Optional.empty();
    }

    @Override
    public String toString() {
        return "ObjectSummaryStatistics [count=" + count + ", min=" + min + ", max=" + max + "]";
    }
}

Using:

    System.out.println( 
            Arrays.stream( new String[] {"one", "two", "абракадабра", "ten", "cat" } )
                .collect( ObjectSummaryStatistics.collector( Comparator.<String>naturalOrder() ) )
        );
// вывод: ObjectSummaryStatistics [count=5, min=cat, max=абракадабра]

    System.out.println( 
            Arrays.stream( new String[] {} )
                .collect( ObjectSummaryStatistics.collector( Comparator.<String>naturalOrder() ) )
        );
// вывод: ObjectSummaryStatistics [count=0, min=null, max=null]

null in the stream is forbidden to remove ambiguity when getting values (Stream.min also doesn't work with null).

 1
Author: zRrr, 2015-11-20 19:11:39

I just struggled with a similar task. Yes, I want to call the min and max methods from the stream, when it becomes clear that only one of the two will be called, there is a desire to split the stream. But that's the catch, as I understand it. The problem statement is such that we learn to find solutions not only directly, but also to look for non-obvious ways. After all, there are several ways to select the minimum and maximum values from any set.

I solved the problem like this: I sorted the stream, converted it to an array, and took the minimum element from the beginning, and the maximum from the end. Here is the code, maybe suboptimal, but the testing system accepted it:

public static <T> void findMinMax(
    Stream<? extends T> stream,
    Comparator<? super T> order,
    BiConsumer<? super T, ? super T> minMaxConsumer) {

Object[] items = stream.sorted(order).toArray();

if (items.length == 0) {
    minMaxConsumer.accept( null, null);    
} else {
    T min = (T) items[0];
    T max = (T) items[items.length - 1];

   minMaxConsumer.accept( min, max);
}

}

 0
Author: Kuznetsov Leonid, 2017-09-23 09:53:45

Course teacher's decision:

public static <T> void findMinMax(
        Stream<? extends T> stream,
        Comparator<? super T> order,
        BiConsumer<? super T, ? super T> minMaxConsumer) {

    MinMaxFinder<T> minMaxFinder = new MinMaxFinder<>(order);
    stream.forEach(minMaxFinder);
    minMaxConsumer.accept(minMaxFinder.min, minMaxFinder.max);
}

private static class MinMaxFinder<T> implements Consumer<T> {

    private final Comparator<? super T> order;
    T min;
    T max;

    private MinMaxFinder(Comparator<? super T> order) {
        this.order = order;
    }

    @Override
    public void accept(T t) {
        if (min == null || order.compare(t, min) < 0) {
            min = t;
        }
        if (max == null || order.compare(max, t) < 0) {
            max = t;
        }
    }
}
 0
Author: irwin, 2019-06-03 10:17:07