How to add elements of two Streams in java?

3
Hello, I would like to do a function that sums the elements of two Streams and returns a Stream with the results, in a "functional" way without using third-party libraries. For example:

BiFunction<Stream,Stream,Stream> mySoma = (s1,s2) -> ?????? ;

I know little of the language and tried to look for something like zipWith or in the last case something to iterate, like get() , but I did not find anything that worked. Is there anything that does this?

(PS: I can switch to IntStream if this helps with anything).

    
asked by anonymous 21.04.2018 / 10:51

1 answer

3

There is no function similar to zipWith in the Streams API of Java. It came to be implemented in a build of Java 8 but was removed because, in the view of those responsible for the language, such functionality would be better provided by libraries, or even each developer could implement its own version that would needs.

Among the issues he cited are:

  • is a language of other languages that does not adapt well to Java because of the inexistence of tuples and other types;
  • In the current version of Java, it is not possible to create a version that accepts primitive types, which can cause performance problems due to boxing and unboxing variables, in addition of the need to create new SAM types ;
  • difficult to parallelize;
  • Multiple ways to handle Streams of different sizes. Which should be chosen?

You can follow the whole discussion about it here .

So you have to implement your own solution, or use some library that has such functionality, such as guava . p>

As a guide, guava implements this method this way :

/**
 * Returns a stream in which each element is the result of passing the corresponding elementY of
 * each of {@code streamA} and {@code streamB} to {@code function}.
 *
 * <p>For example:
 *
 * <pre>{@code
 * Streams.zip(
 *   Stream.of("foo1", "foo2", "foo3"),
 *   Stream.of("bar1", "bar2"),
 *   (arg1, arg2) -> arg1 + ":" + arg2)
 * }</pre>
 *
 * <p>will return {@code Stream.of("foo1:bar1", "foo2:bar2")}.
 *
 * <p>The resulting stream will only be as long as the shorter of the two input streams; if one
 * stream is longer, its extra elements will be ignored.
 *
 * <p>Note that if you are calling {@link Stream#forEach} on the resulting stream, you might want
 * to consider using {@link #forEachPair} instead of this method.
 *
 * <p><b>Performance note:</b> The resulting stream is not <a
 * href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>.
 * This may harm parallel performance.
 */
 public static <A, B, R> Stream<R> zip(
      Stream<A> streamA, Stream<B> streamB, BiFunction<? super A, ? super B, R> function) {
    checkNotNull(streamA);
    checkNotNull(streamB);
    checkNotNull(function);
    boolean isParallel = streamA.isParallel() || streamB.isParallel(); // same as Stream.concat
    Spliterator<A> splitrA = streamA.spliterator();
    Spliterator<B> splitrB = streamB.spliterator();
    int characteristics =
        splitrA.characteristics()
            & splitrB.characteristics()
            & (Spliterator.SIZED | Spliterator.ORDERED);
    Iterator<A> itrA = Spliterators.iterator(splitrA);
    Iterator<B> itrB = Spliterators.iterator(splitrB);
    return StreamSupport.stream(
            new AbstractSpliterator<R>(
                Math.min(splitrA.estimateSize(), splitrB.estimateSize()), characteristics) {
              @Override
              public boolean tryAdvance(Consumer<? super R> action) {
                if (itrA.hasNext() && itrB.hasNext()) {
                  action.accept(function.apply(itrA.next(), itrB.next()));
                  return true;
                }
                return false;
              }
            },
            isParallel)
        .onClose(streamA::close)
        .onClose(streamB::close);
}

Note: The checkNotNull method is also part of guava. If you want to use such an implementation without using the library, you can replace it with the Objects.requireNonNull method.

    
21.04.2018 / 14:20