Basically, a tuple is a data structure consisting of multiple parts. Usually, a tuple has two or three parts. Typically, when more than three parts are needed, a dedicated class is a better choice.
Tuples are immutable and are used whenever we need to return multiple results from a method. For example, let's assume that we have a method that returns the minimum and maximum of an array. Normally, a method cannot return both, and using a tuple is a convenient solution.
Unfortunately, Java doesn't provide built-in tuple support. Nevertheless, Java comes with Map.Entry<K,V>, which is used to represent an entry from a Map. Moreover, starting with JDK 9, the Map interface was enriched with a method named entry(K k, V v), which returns an unmodifiable Map.Entry<K, V> containing the given key and value.
For a tuple of two parts, we can write our method as follows:
public static <T> Map.Entry<T, T> array(
T[] arr, Comparator<? super T> c) {
T min = arr[0];
T max = arr[0];
for (T elem: arr) {
if (c.compare(min, elem) > 0) {
min = elem;
} else if (c.compare(max, elem)<0) {
max = elem;
}
}
return entry(min, max);
}
If this method lives in a class named Bounds, then we can call it as follows:
public class Melon {
private final String type;
private final int weight;
// constructor, getters, equals(), hashCode(),
// toString() omitted for brevity
}
Melon[] melons = {
new Melon("Crenshaw", 2000), new Melon("Gac", 1200),
new Melon("Bitter", 2200), new Melon("Hami", 800)
};
Comparator<Melon> byWeight = Comparator.comparing(Melon::getWeight);
Map.Entry<Melon, Melon> minmax = Bounds.array(melons, byWeight);
System.out.println("Min: " + minmax1.getKey()); // Hami(800g)
System.out.println("Max: " + minmax1.getValue()); // Bitter(2200g)
But we can write an implementation as well. A tuple with two parts is commonly named a pair; therefore, an intuitive implementation can be as follows:
public final class Pair<L, R> {
final L left;
final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
static <L, R> Pair<L, R> of (L left, R right) {
return new Pair<>(left, right);
}
// equals() and hashCode() omitted for brevity
}
Now, we can rewrite our method that computes the minimum and maximum as follows:
public static <T> Pair<T, T> array(T[] arr, Comparator<? super T> c) {
...
return Pair.of(min, max);
}