How to implement unpacking tuples for function parameters in Java?

I created several tuple classes and put them in the Java collection. But I do not want to use tuple as function parameters directly during iteration of the collection. Therefore, I implemented tuple unpacking as the following code.

This basically works, but the problem is that type listing is required:

map((Func2<Long, Long, Long>) (a, b) -> a + b)

Is there any way to remove type casting here?


change

Perhaps I didn’t make it clear that you need to support not only tuple2 , but also tuple3 , tuple4 .... @Flown's answer works significantly for tuple2 , but does not work for tuple2 , tuple3 , tuple4 in the meantime

 package test; import com.google.common.collect.Iterables; import java.util.Arrays; import java.util.function.Function; import static test.TupleIterable.Tuple.tuple; public interface TupleIterable<T> { Iterable<T> apply(); static <E> TupleIterable<E> from(Iterable<E> iterable) { return () -> iterable; } default <E> TupleIterable<E> map(Function<? super T, ? extends E> op) { return () -> Iterables.transform(TupleIterable.this.apply(), op::apply); } interface Func2<T1, T2, R> extends Function<Tuple.Tuple2<T1, T2>, R> { R apply(T1 t1, T2 t2); @Override default R apply(Tuple.Tuple2<T1, T2> t) { return apply(t.t1, t.t2); } } interface Func3<T1, T2, T3, R> extends Function<Tuple.Tuple3<T1, T2, T3>, R> { R apply(T1 t1, T2 t2, T3 t3); @Override default R apply(Tuple.Tuple3<T1, T2, T3> t) { return apply(t.t1, t.t2, t.t3); } } interface Tuple { static <T1, T2> Tuple2<T1, T2> tuple(T1 t1, T2 t2) { return new Tuple2<>(t1, t2); } static <T1, T2, T3> Tuple3<T1, T2, T3> tuple(T1 t1, T2 t2, T3 t3) { return new Tuple3<>(t1, t2, t3); } class Tuple2<T1, T2> implements Tuple { public T1 t1; public T2 t2; public Tuple2(T1 t1, T2 t2) { this.t1 = t1; this.t2 = t2; } } class Tuple3<T1, T2, T3> implements Tuple { public T1 t1; public T2 t2; public T3 t3; public Tuple3(T1 t1, T2 t2, T3 t3) { this.t1 = t1; this.t2 = t2; this.t3 = t3; } } } public static void main(String[] args) { TupleIterable.from(Arrays.asList(1L, 2L)) .map(x -> tuple(x, x)) // map long to tuple2 .map((Func2<Long, Long, Tuple.Tuple3<Long, Long, Long>>) (a, b) -> tuple(a, b, a + b)) // map tuple2 to tuple3 .map((Func3<Long, Long, Long, Long>) (a, b, c) -> a + b + c) // map tuple3 to Long .apply() .forEach(System.out::println); } } 
+5
source share
3 answers

You can do the same trick as the JDK developer to specialize Stream for primitive types. We introduce mapToTuple and unwrap in different interfaces.

I rewrote part of your code and used some existing FunctionalInterfaces .

 import java.util.Arrays; import java.util.Iterator; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; public class Test { public static void main(String... args) { MyIterable.from(Arrays.asList(1L, 2L)).mapToTuple(l -> Tuple2.tuple(l, l + 1L)).unwrap((a, b) -> a + b).get() .forEach(System.out::println); } } final class Tuple2<T1, T2> { public static <T1, T2> Tuple2<T1, T2> tuple(T1 t1, T2 t2) { return new Tuple2<>(t1, t2); } public final T1 t1; public final T2 t2; private Tuple2(T1 t1, T2 t2) { this.t1 = t1; this.t2 = t2; } } @FunctionalInterface interface TupleIterable<T1, T2> extends Supplier<Iterable<Tuple2<T1, T2>>> { default <E> MyIterable<E> unwrap(BiFunction<T1, T2, E> func) { return () -> Iterables.transform(get(), t -> func.apply(t.t1, t.t2)); } } @FunctionalInterface interface MyIterable<T> extends Supplier<Iterable<T>> { static <E> MyIterable<E> from(Iterable<E> iterable) { return () -> iterable; } default <E> MyIterable<E> map(Function<? super T, ? extends E> mapper) { return () -> Iterables.transform(get(), mapper::apply); } default <T1, T2> TupleIterable<T1, T2> mapToTuple(Function<? super T, ? extends Tuple2<T1, T2>> tupleMapper) { return () -> Iterables.transform(get(), tupleMapper::apply); } } final class Iterables { public static <T, E> Iterable<E> transform(Iterable<T> iterable, Function<? super T, ? extends E> mapper) { return () -> new Iterator<E>() { private final Iterator<T> iter = iterable.iterator(); @Override public boolean hasNext() { return iter.hasNext(); } @Override public E next() { return mapper.apply(iter.next()); } }; } } 
+3
source

Instead of doing type casting, I created an unpack function to unpack tuples. It works like map(unpack((a, b) -> tuple(a, b, a + b))) , although it is not quite intuitive.

 package test; import com.google.common.collect.Iterables; import java.util.Arrays; import java.util.function.Function; import static test.TupleIterable.Func.unpack; import static test.TupleIterable.Tuple.tuple; public interface TupleIterable<T> { Iterable<T> apply(); static <E> TupleIterable<E> from(Iterable<E> iterable) { return () -> iterable; } default <E> TupleIterable<E> map(Function<? super T, E> op) { return () -> Iterables.transform(TupleIterable.this.apply(), op::apply); } interface Func { static <T1, T2, R> Function<Tuple.Tuple2<T1, T2>, R> unpack(Func2<T1, T2, R> f) { return t -> f.apply(t.t1, t.t2); } static <T1, T2, T3, R> Function<Tuple.Tuple3<T1, T2, T3>, R> unpack(Func3<T1, T2, T3, R> f) { return t -> f.apply(t.t1, t.t2, t.t3); } @FunctionalInterface interface Func2<T1, T2, R> { R apply(T1 t1, T2 t2); } @FunctionalInterface interface Func3<T1, T2, T3, R> { R apply(T1 t1, T2 t2, T3 t3); } } interface Tuple { static <T1, T2> Tuple2<T1, T2> tuple(T1 t1, T2 t2) { return new Tuple2<>(t1, t2); } static <T1, T2, T3> Tuple3<T1, T2, T3> tuple(T1 t1, T2 t2, T3 t3) { return new Tuple3<>(t1, t2, t3); } class Tuple2<T1, T2> implements Tuple { public T1 t1; public T2 t2; public Tuple2(T1 t1, T2 t2) { this.t1 = t1; this.t2 = t2; } } class Tuple3<T1, T2, T3> implements Tuple { public T1 t1; public T2 t2; public T3 t3; public Tuple3(T1 t1, T2 t2, T3 t3) { this.t1 = t1; this.t2 = t2; this.t3 = t3; } } } public static void main(String[] args) { TupleIterable.from(Arrays.asList(1L, 2L)) .map(x -> tuple(x, x)) // map long to tuple2 .map(unpack((a, b) -> tuple(a, b, a + b))) // map tuple2 to tuple3 .map(unpack((a, b, c) -> a + b + c)) // map tuple3 to Long .apply() .forEach(System.out::println); } } 
+1
source

I can’t say if this is too dirty or not, but why not provide getters for T1 and T2 inside Tuple2 and do:

 .map(x -> tuple(x, x + 1)) // map long to tuple2 .map(a -> a.getT1() + a.getT2()) // map tuple2 to long 
0
source

Source: https://habr.com/ru/post/1268546/


All Articles