Using a Pair/Tuple type in Java

At times I find myself wanting to use a type such as tuple in Python (or Scala) or the std::pair from C++ to represent a listing of homogenous or heterogenous values e.g. coordinates (x,y), a value along with its frequency ('b',2) or some other type of state involving multiple values. There's 4 different ways I've found to accomplish this in Java without needing external libraries (mostly).
Custom class
Map.EntryRecords (JDK 14+)
javafx.util.Pairclass (JDK 11 and below)
Note: the same simple example will be used throughout - returning the average score along with letter grade from a student's test scores. Also text blocks for printing output, which are included from JDK 15+
Custom Class
Define a class containing the amount of items (2 in this case for a pair) and their types.
// Definition
class Tuple<K,V> {
private final K first;
private final V second;
public Tuple(K first, V second){
this.first = first;
this.second = second;
}
public K first(){return first;}
public V second(){return second;}
// other methods, etc ...
}
// Usage
class Main {
public static void main(String[] args) {
var studentId = "123-abc";
var studentScores = fetchScores(studentId);
var gradeTuple = calcGrade(studentScores);
var msg = "Grade for student %s: %s(%.2f)".formatted(studentId, gradeTuple.first(), gradeTuple.second());
System.out.println(msg);
// e.g. Grade for student 123-abc: A(95.5)
}
public Pair<String, Double> calcGrade(List<Double> studentScores) {
double avg = studentScores.stream()
.mapToDouble(Double::doubleValue)
.average().getAsDouble();
String letterGrade = getGradeFor(score);
return new Tuple<String, Double>(letterGrade, avg);
}
}
Pros π
- Relatively quick to define a custom class, and you can customize it exactly to your needs.
Cons π
- Compared to a record, there will be some extra effort needed to implement methods such as
equals(),hashcode(), etc (if class instances need to be compared or sorted)
Map.Entry
Utilize the Map.Entry interface (a nested interface of Map). It represents a single entry from a map i.e. a (key, value) "pairing". In the example below I use the static Map.entry(key,value) method to return an implementation.
class Main {
public static void main(String[] args) {
var studentId = "123-abc";
var studentScores = fetchScores(studentId);
var gradeEntry = calcGrade(studentScores);
var msg = "Grade for student %s: %s(%.2f)".formatted(studentId, gradeEntry.getKey(), gradeEntry.getValue());
// e.g. Grade for student 123-abc: A(95.5)
System.out.println(msg);
}
public Map.Entry<String, Double> calcGrade(List<Double> studentScores) {
double avg = studentScores.stream()
.mapToDouble(Double::doubleValue)
.average().getAsDouble();
String letterGrade = getGradeFor(score);
return Map.entry(letterGrade, avg);
}
}
Pros π
Map.Entryis included in every JDK and there's no need to create additional classes
Cons π
- The semantics might be a little "odd" if what you are trying to model does not have a key/value relationship
Records
Use the record class introduced in JDK 14. It allows for the quick creation of immutable classes that include toString(), hashCode(), and equals() "out of the box". They can be declared locally, or as class variables.
class Main {
record Tuple<K,V>(K first, V second){}
public static void main(String[] args) {
var studentId = "123-abc";
var studentScores = fetchScores(studentId);
var gradeTuple = calcGrade(studentScores);
var msg = "Grade for student %s: %s(%.2f)".formatted(studentId, gradeTuple.first(), gradeTuple.second());
// e.g. Grade for student 123-abc: A(95.5)
System.out.println(msg);
}
public Map.Entry<String, Double> calcGrade(List<Double> studentScores) {
double avg = studentScores.stream()
.mapToDouble(Double::doubleValue)
.average().getAsDouble();
String letterGrade = getGradeFor(score);
return new Tuple<String, Double>(letterGrade, avg);
}
}
Pros π
Much less code needs to be written compared to defining a custom class
Records are immutable by default (helps avoid making unintended changes)
Cons π
- Records aren't available in Java versions older than 14
javafx.util.Pair
Use the Pair class from the javafx package, which was included in the JDK until version 11.
class Main {
public static void main(String[] args) {
var studentId = "123-abc";
var studentScores = fetchScores(studentId);
var gradePair = calcGrade(studentScores);
var msg = "Grade for student %s: %s(%.2f)".formatted(studentId, gradeTuple.first(), gradeTuple.second());
// e.g. Grade for student 123-abc: A(95.5)
System.out.println(msg);
}
public Pair<String, Double> calcGrade(List<Double> studentScores) {
double avg = studentScores.stream()
.mapToDouble(Double::doubleValue)
.average().getAsDouble();
String letterGrade = getGradeFor(score);
return new Pair<String, Double>(letterGrade, avg);
}
}
Pros π
Functionality provided out of the box
Built-in
equals(),toString(), etc
Cons π
- Pair class is not included in JDK versions after 11
Comments
Join the discussionNo comments yet. Be the first to comment.