简体   繁体   中英

Combine two streams with collation

I need to do some Matrix work in an efficient and flexible way and hoped I could practice my Java 8 using streams and lambdas and perhaps even get free parallelism out of it. One point I am struggling with is how to perform an operation on two streams putting the result into a third.

Consider the simple mechanism:

static final List<String> a = Arrays.asList("A", "A", "A");
static final List<String> b = Arrays.asList("B", "B", "B");

public void withoutStreams() {
    // The boring old way.
    List<String> c = new ArrayList<>();
    for (Iterator<String> ai = a.iterator(), bi = b.iterator(); ai.hasNext() && bi.hasNext();) {
        c.add(ai.next() + bi.next());
    }
    System.out.println(c);
}

Works fine but I want to use Streams.

private void withStreams() {
    List<String> c = new ArrayList<>();
    combine(a.stream(), b.stream(), c, (String x, String y) -> x + y);
}

private void combine(Stream<String> a, Stream<String> b, List<String> c, BinaryOperator<String> op) {
    // What can I do here to make it happen?
}

I fully expect we will be filling c using a Consumer of some form but extra kudos for coming up with some way of referring to a specific cell of the matrix other than using (row,col) bearing in mind that the cells will be immutable.

You can use the IntStream class to simulate indexing and then .mapToObj to concatenate the corresponding to the index objects from a and b :

List<String> list = IntStream.range(0, Math.max(a.size(), b.size()))
                             .mapToObj(i -> a.get(i) + b.get(i))
                             .collect(Collectors.toList());

Applied to your method, this will look like:

private void combine(List<String> a, List<String> b, List<String> c, BinaryOperator<String> op) {
    c = IntStream.range(0, Math.max(a.size(), b.size()))
                 .mapToObj(i -> op.apply(a.get(i), b.get(i)))
                 .collect(Collectors.toList());
}

However, if you don't want to change the method's signature, here is a solution which works for all possible combinations of infinite and finite streams :

private void combine(Stream<String> a, Stream<String> b, List<String> c, BinaryOperator<String> op) {
    Iterator<String> i1 = a.iterator();
    Iterator<String> i2 = b.iterator();
    Iterable<String> i = () -> new Iterator<String>() {
        public boolean hasNext() {
            return i1.hasNext() && i2.hasNext();
        }
        public String next() {
            return op.apply(i1.next(), i2.next());
        }
    };
    c = StreamSupport.stream(i.spliterator(), false).collect(Collectors.toList());
}

Functional programming style, using recursive (no loop):

static Stream<String> combine(List<String> a, List<String> b) {
    if(a.isEmpty() ||  b.isEmpty()) {
        return Stream.empty();
    }

    return Stream.concat(
            Stream.of(a.get(0) + b.get(0)),
            combine(a.stream().skip(1).collect(Collectors.toList()), 
                    b.stream().skip(1).collect(Collectors.toList()))
            );
}

plus: I vote up kocko's answer, my answer is for fun.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM