1. Overview
In Java, we often have to work with data in pairs, and the Apache Commons Lang3 library provides a convenient Pair class for this purpose. When we have a list of Pair<String, Integer>, there are many cases where we need to sort it by the integer value.
In this tutorial, we’ll explore various ways to sort a List of Apache Commons Lang3’s Pair<String, Integer> by the integer part.
2. Introduction to the Problem
The Pair class in Apache Commons Lang3 provides a simple structure for holding two values. As it’s one of the commonly used Pair types in Java, we’ll use it as an example in this tutorial.
As usual, let’s understand the problem through examples. Let’s first create a method to build up a List of Apache Commons Lang3’s Pair<String, Integer> objects:
private List<Pair<String, Integer>> getUnsortedInput() {
return Arrays.asList(
Pair.of("False", 5),
Pair.of("Yes", 3),
Pair.of("True", 4),
Pair.of("No", 2),
Pair.of("X", 1)
);
}
As the code above shows, the getUnsortedInput() method produces an unsorted List<Pair<String, Integer>>. Each Pair element holds two values: a String and the Integer count of letters it contains.
We aim to sort the List by each Pair element’s integer value. Therefore, the expected result is:
static final List<Pair<String, Integer>> EXPECTED = List.of(
Pair.of("X", 1),
Pair.of("No", 2),
Pair.of("Yes", 3),
Pair.of("True", 4),
Pair.of("False", 5)
);
Next, we’ll explore different approaches to solving this interesting sorting problem.
3. Using an Anonymous Comparator Class
We know that we need to compare elements to sort a collection of data. Apache Commons Lang3’s Pair class doesn’t implement the Comparable interface. Therefore, we cannot directly compare two Pair objects.
However, *we can create an anonymous class that implements the Comparator interface to compare two Pairs’ integer values and pass the Comparator to List.sort():*
List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort(new Comparator<Pair<String, Integer>>() {
@Override
public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
return o1.getRight()
.compareTo(o2.getRight());
}
});
assertEquals(EXPECTED, myList);
As the code above shows, the anonymous Comparator class implements the compare() method. Since Integer implements Comparable, we compare the integer values of the two Pair elements using Integer.compareTo().
It’s worth mentioning that Apache Commons Lang3’s Pair class provides getRight() and getValue() methods to return the second value in a Pair object. There is no difference between these two methods. Actually, getValue() invokes the getRight() method:
public abstract R getRight();
public R getValue() {
return this.getRight();
}
When we run the test, it passes. Therefore, this approach solves the problem.
4. Using a Lambda Expression
The anonymous Comparator class approach solves our sorting problem. However, the anonymous class code isn’t easy to read. As of Java 8, the List.sort() method provides a functional possibility: lambda expression as Comparator support.
Next, let’s refactor the anonymous Comparator class solution to a comparison with lambda expression:
List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort((p1, p2) -> p1.getRight()
.compareTo(p2.getRight()));
assertEquals(EXPECTED, myList);
As we can see, the lambda expression approach provides a concise way of implementing the Comparator interface. It makes the code more readable and easier to maintain.
5. Using Comparator.comparing()
Since Java 8, the Comparator interface offers a new comparing() method. This method accepts a Function keyExtractor and returns a Comparator that compares by that sort key.
Next, let’s employ the Comparator.comparing() method to solve our sorting problem:
List<Pair<String, Integer>> myList = getUnsortedInput();
myList.sort(Comparator.comparing(Pair::getRight));
assertEquals(EXPECTED, myList);
In this example, we pass the method reference Pair::getRight to the comparing() method as the functional parameter. It creates a Comparator object that sorts Pair elements in the given List<Pair<String, Integer>> by the integer value.
6. A New List for the Sorted Result
We’ve seen three solutions to the sorting problem. It’s important to note that all three approaches perform an in-place sort, which changes the element order in the original input List.
However, sometimes we cannot modify the input list — for instance, if our input is an immutable List:
List<Pair<String, Integer>> immutableUnsortedList = List.copyOf(getUnsortedInput());
assertThrows(UnsupportedOperationException.class,
() -> immutableUnsortedList.sort(Comparator.comparing(Pair::getRight)));
In this example, we use List.copyOf() to create an immutable List, and an exception is thrown when we sort it using a previous solution.
Therefore, we’ll have to obtain the sorted result as a new List object.
A straightforward idea to achieve that is first to make a copy of the original List and then sort the copied List in place.
Alternatively, we can use Stream‘s sorted() method to perform sorting and then collect the sorted elements into a new List object. Let’s see this in action:
List<Pair<String, Integer>> immutableUnsortedList = List.copyOf(getUnsortedInput());
List<Pair<String, Integer>> sorted = immutableUnsortedList.stream()
.sorted(Comparator.comparing(Pair::getRight))
.toList();
assertEquals(EXPECTED, sorted);
As we can see, the stream().sorted().toList() pipeline is easy to read and solves the problem fluently.
7. Conclusion
In this article, we’ve explored various approaches to sort a List of Pair<String, Integer> elements by each Pair‘s integer value. Also, we discussed how to obtain a new List object for the sorted result and keep the original List unmodified.
As always, the complete source code for the examples is available over on GitHub.