Combining streams in Java

July 31, 2013

Given an interface Stream:

interface Stream<A> {
  A head();
  Stream<A> tail();
}

Constant streams

Write the functions zeroes, ones, and twos, which return streams of values 0, 1, and 2:

Stream<Integer> zeroes() {
  return ???; // 0 -> 0 -> 0 -> 0 -> ...
}

Stream<Integer> ones() {
  return ???; // 1 -> 1 -> 1 -> 1 -> ...
}

Stream<Integer> twos() {
  return ???; // 2 -> 2 -> 2 -> 2 -> ...
}

Natural numbers streams

Write the functions evens and odds, which return streams of the even natural numbers (0, 2, 4, ...) and the odd natural numbers (1, 3, 5, ...):

Stream<Integer> evens() {
  return ???; // 0 -> 2 -> 4 -> 6 -> ...
}

Stream<Integer> odds() {
  return ???; // 1 -> 3 -> 5 -> 7 -> ...
}

Combined streams

Write the function combine, which interleaves its argument streams in constant time:

static <A> Stream<A> combine(Stream<A>... ss) {
  return ???; // combine(evens(), odds()) = 0 -> 1 -> 2 -> 3 -> ...
}

Solution

Constants streams

class ConstantsStreams {

  static class ConstantsStream implements Stream<Integer> {

    private final int value;

    public ConstantsStream(int value) {
      this.value = value;
    }

    public Integer head() {
      return value;
    }

    public Stream<Integer> tail() {
      return this;
    }
  }

  static Stream<Integer> zeroes() {
    return new ConstantsStream(0);
  }

  static Stream<Integer> ones() {
    return new ConstantsStream(1);
  }

  static Stream<Integer> twos() {
    return new ConstantsStream(2);
  }
}

Natural numbers streams

class NatsStreams {

  static class ByTwosStream implements Stream<Integer> {

    private final int value;

    public ByTwosStream(int value) {
      this.value = value;
    }

    public Integer head() {
      return value;
    }

    public Stream<Integer> tail() {
      return new ByTwosStream(value + 2);
    }

  }

  static Stream<Integer> evens() {
    return new ByTwosStream(0);
  }

  static Stream<Integer> odds() {
    return new ByTwosStream(1);
  }
}

Combined streams

class CombinedStreams {

  static class InterleavedStream<A> implements Stream<A> {

    private final Stream<A>[] ss;

    public InterleavedStream(Stream<A>... ss) {
      this.ss = ss;
    }

    public A head() {
      return ss[0].head();
    }

    public Stream<A> tail() {
      Stream<A>[] tailSS = new Stream[ss.length];
      for (int i = 0; i < ss.length - 1; i++) {
        tailSS[i] = ss[i + 1];
      }
      tailSS[tailSS.length - 1] = ss[0].tail();
      return new InterleavedStream<A>(tailSS);
    }

  }

  static <A> Stream<A> combine(Stream<A>... ss) {
    return new InterleavedStream<>(ss);
  }
}

Demo

class Streams {

  static <A> String peek(Stream<A> s) {
    return s.head()                      + " -> " +
           s.tail().head()               + " -> " +
           s.tail().tail().head()        + " -> " +
           s.tail().tail().tail().head() + " -> ...";
  }

  public static void main(String[] args) {

    System.out.println();
    System.out.println("Constants streams:");
    System.out.println("zeroes() = " + peek(ConstantsStreams.zeroes()));
    System.out.println("  ones() = " + peek(ConstantsStreams.ones()));
    System.out.println("  twos() = " + peek(ConstantsStreams.twos()));

    System.out.println();
    System.out.println("Natural numbers streams:");
    System.out.println("evens() = " + peek(NatsStreams.evens()));
    System.out.println(" odds() = " + peek(NatsStreams.odds()));

    System.out.println();
    System.out.println("Combined streams:");
    System.out.println("combine(evens(), odds()) = " +
      peek(CombinedStreams.combine(NatsStreams.evens(), NatsStreams.odds()))
    );
  }

}

This file is literate Java, and can be run using Codedown:

$ curl https://earldouglas.com/posts/exercises/java-streams.md |
  codedown java > Streams.java
$ javac Streams.java
$ java Streams

Constants streams:
zeroes() = 0 -> 0 -> 0 -> 0 -> ...
  ones() = 1 -> 1 -> 1 -> 1 -> ...
  twos() = 2 -> 2 -> 2 -> 2 -> ...

Natural numbers streams:
evens() = 0 -> 2 -> 4 -> 6 -> ...
 odds() = 1 -> 3 -> 5 -> 7 -> ...

Combined streams:
combine(evens(), odds()) = 0 -> 1 -> 2 -> 3 -> ...