Fork me on GitHub

Table of contents

Pair and Triple

UML UML

Pair and Triple are immutable heterogeneous tuples (for n=2,3) that can be simply used to hold values together:

final var p = Pair.of("foo bar", 7);
final var fooBar = p.a;
final var seven = p.b;
final var t = Triple.of("foo bar", 7, false);
final var fooBar = t.a;
final var seven = t.b;
final var bool = t.c;

Both Pair and Triple offer methods to generate special Streams. For instance, this code:

final var stream1 = Pair.pairRangeClosed("foo bar", 1, 3);

generates pairs ["foo bar", 1], ["foo bar", 2], ["foo bar", 3]. The following code:

final var stream2 = Pair.indexedPairStream(List.of("foo", "bar"));

generates pairs [0, "foo"], [1, "bar"]. Variants allow to start from custom Streams, Collections and Iterables, or to pick a different value for the starting index.

Pair can be used to easily implement a two-level nested loop with Streams. For instance, the following code:

final var actual =  IntStream.rangeClosed(1, limit)
                             .boxed()
                             .flatMap(a -> Pair.pairRangeClosed(a, a + 1, limit))
                             .collect(toList());

is equivalent to this two-levels nested loop:

        final List<Pair<Integer, Integer>> expected = new ArrayList<>();

        for (var a = 1; a <= limit; a++)
          {
            for (var b = a + 1; b <= limit; b++)
              {
                expected.add(Pair.of(a, b));
              }
          }

In a similar way, this code with Triple:

final var actual = IntStream.rangeClosed(1, limit)
                            .boxed()
                            .flatMap(a -> Pair.pairRangeClosed(a, a + 1, limit))
                            .flatMap(p -> Triple.tripleRangeClosed(p, p.b + 1, limit))
                            .collect(toList());

is equivalent to this three-levels nested loop:

        final List<Triple<Integer, Integer, Integer>> expected = new ArrayList<>();

        for (var a = 1; a <= limit; a++)
          {
            for (var b = a + 1; b <= limit; b++)
              {
                for (var c = b + 1; c <= limit; c++)
                  {
                    expected.add(Triple.of(a, b, c));
                  }
              }
          }

It is also possible to “zip” two streams into a Stream<Pair>:

// given
final var intStream = IntStream.range(0, 5).boxed();
final var stringStream = IntStream.range(0, 5).mapToObj(n -> "string-" + (char)('a' + n));
// when
final var underTest = Pair.zip(intStream, stringStream);
// then
assertThat(underTest.collect(toList())).containsExactly(Pair.of(0, "string-a"),
                                                        Pair.of(1, "string-b"),
                                                        Pair.of(2, "string-c"),
                                                        Pair.of(3, "string-d"),
                                                        Pair.of(4, "string-e"));