How to implement Java 8 generic method that returns zipped stream where objects from two streams appear in turn?

michalk :

I tried to implement a generic method that returns a zipped stream from two streams. The returned stream should be a concatenation of two streams where elements appear in turn, and are based on two streams that are passed to this method. If one stream is longer than another, it should contain values from the longer stream in the end. The code I have come up with is:

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Stream;

public class Task12 {

public static void main(String[] args) {

    Stream<Integer> stream1 = Stream.iterate(0, integer -> integer)
            .limit(20);

    Stream<Integer> stream2 = Stream.iterate(1, integer -> integer)
            .limit(20);

    Stream<Integer> zippedStreams = zip(stream1, stream2);

    zippedStreams.forEach(System.out::println);

}

private static <T> Stream<T> zip(Stream<T> first, Stream<T> second) {

    Iterator<T> iterator1 = first.iterator();
    Iterator<T> iterator2 = second.iterator();

    List<T> elements = new LinkedList<>();


    while (iterator1.hasNext() || iterator2.hasNext()) {
        if (iterator1.hasNext()) {
            elements.add(iterator1.next());
        }

        if(iterator2.hasNext()) {
            elements.add(iterator2.next());
        }
    }

    return elements.stream();

}

I have a few questions about this implementation :

  1. is there a better way to implement it?
  2. what if one of the streams would be an infinite Stream? Is there a way to limit the returned stream to contain exact number of elements - other than using "limit" method and passing limit as a parameter to "zip" method?
Wisthler :

There is a way to construct the stream out of an iterator/iterable. That's very close to your solution but that way, it handles infinite stream also

public static void main(String[] args) {

        Stream<Integer> stream1 = Stream.iterate(0, integer -> integer);

        Stream<Integer> stream2 = Stream.iterate(1, integer -> integer)
                .limit(4);

        Stream<Integer> zippedStreams = StreamSupport.stream(new Ziperator<>(stream1, stream2).spliterator(), false);

        zippedStreams.limit(15).forEach(System.out::println);

    }


    public static class Ziperator<T> implements Iterator<T>, Iterable<T>{
        Iterator<T> iterator1;
        Iterator<T> iterator2;
        boolean even = true;


        @Override
        public Iterator<T> iterator() {
            return this;
        }

        public Ziperator(Stream<T> first, Stream<T> second) {
            iterator1 = first.iterator();
            iterator2 = second.iterator();
        }

        public boolean hasNext(){
            return iterator1.hasNext() || iterator2.hasNext();
        }

        @Override
        public T next() {
            if(!iterator2.hasNext()){
                even = true;
            }
            if(!iterator1.hasNext()){
                even = false;
            }
            if (even) {
                even = false;
                return iterator1.next();
            } else {
                even = true;
                return iterator2.next();
            }
        }

        @Override
        public void remove() {

        }
    }

Output for that is

0
1
0
1
0
1
0
1
0
0
0
0
0
0
0

This code can be clean-up/optimized but it's a working base :)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=146055&siteId=1