Search code examples
javajava-8tuplesapache-commons-lang3

Helper methods for creating tuples and tupled functions in Java?


I'm using Java 8 along with the Pair from Apache Commons Lang3.

The first thing I am trying to do is get a stream from a List<T> and to take a Function<T,U> and ultimately create a List<Pair<T,U>>. Currently I am creating a Function<T,Pair<T,U>> with the specific types I want and using this to map the stream. What I want is something like:

public static <T, U> Function<T, Pair<T, U>> tupledResult(final Function<T, U> generator) {
    Objects.requireNonNull(generator);
    return (T t) -> new ImmutablePair<>(t, generator.apply(t));
}

The next problem is that now that I have a List<Pair<T, U>> I want to be able to use foreach to apply a BiConsumer<T,U> (similar to the tupled function in Scala). I guess it would look like:

public static <T, U> Consumer<Pair<T, U>> tupled(final BiConsumer<T, U> consumer) {
    Objects.requireNonNull(consumer);
    return (final Pair<T, U> p) -> consumer.accept(p.getLeft(), p.getRight());
}

Is there anything in Apache Commons Lang3 that does this or should I roll my own? If the later, is this something that is useful to contribute or is this a bad solution?

Example

This is the sort of thing I want to achieve:

private void doStuff(final List<Thing> things) {
    final List<Pair<Thing, Other>> otherThings = things.stream()
        .map(tupledResult(ThingHelper::generateOther))
        .collect(Collectors.toList());
    otherThings.stream().forEach(tupled((final Thing thing, final Other other) -> {
        doSomething(thing, other);
    }));
    otherThings.stream().forEach(tupled((final Thing thing, final Other other) -> {
        doSomethingElse(thing, other);
    }));
}

The points here are that ThingHelper.generateOther is relatively expensive and I only want to do it once. Also doSomething must be applied to everything first and then doSomethingElse afterwards.

The pairs never leave the scope of this method nor do I want to overload the methods to take a pair. In this case I really don't care about the lack of semantics of the pair, all that matters is the order. doSomething and doSomethingElse are the ones providing the semantics.


Solution

  • Such methods are absent in Apache Commons Lang3 as this library is Java 6 compatible, but the methods you want must return objects of java.util.function package which appeared only in Java 8.

    If your Thing objects are not repeating, it's quite natural in your case to use Map instead:

    private void doStuff(final List<Thing> things) {
        final Map<Thing, Other> otherThings = things.stream()
            .collect(Collectors.toMap(Function.identity(), ThingHelper::generateOther));
        otherThings.forEach((final Thing thing, final Other other) -> {
            doSomething(thing, other);
        });
        otherThings.forEach((final Thing thing, final Other other) -> {
            doSomethingElse(thing, other);
        });
    }
    

    Or even shorter:

    private void doStuff(List<Thing> things) {
        Map<Thing, Other> otherThings = things.stream()
            .collect(toMap(x -> x, ThingHelper::generateOther));
        otherThings.forEach(this::doSomething);
        otherThings.forEach(this::doSomethingElse);
    }
    

    This way you don't need wrappers as Map.forEach already accepts BiConsumer and Collectors.toMap second parameter essentially replaces your tupledResult.