Introduction
The Apache Commons Lang Pair contains two elements. In this post, we have seen how to use the Apache Commons Lang Pair class.
Importing Apache Commons Lang
In you are using Maven, you can import the Commons Lang as:
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
Replace 3.12.0 with the latest version available from Maven.
For Gradle,
implementation 'org.apache.commons:commons-lang3:3.12.0'
Apache Commons Lang Pair
A pair contains two elements. They are referred to as the left and the right. The Pair is an abstract class with two implementations viz., ImmutablePair and MutablePair. The Pair class also implements the Map.Entry interface and hence the key is the left and the value is the right.
Let us first look at creating Immutable Pair instances using the static factory methods in the Pair class. Then we will look at the two subclasses of the Pair class.
Apache Commons Lang Pair class methods
Creating a pair instance
We can create an immutable pair using one of the two overloaded of() methods in the Pair class. We can pass the key and value (left and right) values to the of() method.
Pair<String, String> pair = Pair.of("Language", "Java");
System.out.println(pair.getLeft());
System.out.println(pair.getRight());
System.out.println(pair.getKey());
System.out.println(pair.getValue());
In the above code, we have created a Pair (ImmutablePair) with key as “Language” and value as “Java”. We can print the key and value (left and right) using the getLeft() and getRight() methods. The getKey() and getValue() methods do the same. Thus, the above code prints,
Language
Java
Language
Java
Since a Pair implements and supports Map.Entry interface, we can pass an instance of Map.Entry to another of() method as shown below.
Pair<String, String> pair = Pair.of(Map.entry("Language", "Java"));
System.out.println(pair.getLeft()); //Language
System.out.println(pair.getRight()); //Java
In the above code, I’ve used the Map.entry static method added in Java 9. To learn about it and other static methods added in Java 9, read the Convenience Factory Methods for Collections post.
For Java 8, one way to create a Map.Entry is to use the AbstractMap.SimpleImmutableEntry class as shown below.
Map.Entry<String, String> entry = new AbstractMap.SimpleImmutableEntry<>("Language", "Java");
Pair<String, String> pair = Pair.of(entry);
System.out.println(pair.getLeft()); //Language
System.out.println(pair.getRight()); //Java
An example of using Pair in a stream and map is shown below.
Map<String, String> map = new HashMap<>();
map.put("Language", "Java");
map.entrySet()
.stream()
.map(Pair::of) //.map(entry -> Pair.of(entry))
.forEach(aPair -> System.out.println(aPair.getLeft() + ": " + aPair.getRight()));
We create a stream of map entries and create a Pair object for each entry and print it. We used a method reference in the map operation.
The resultant output is:
Language: Java
Printing a pair (toString)
The default string representation of a Pair is ($left,$right) where $left is the left value and $right is the right value.
Pair<String, String> pair = Pair.of(Map.entry("Language", "Java"));
System.out.println(pair); //(Language,Java)
But we can specify the format to use for printing the values. In the format, we specify the left and right values as %1$s and %2$s, respectively.
In the below example, we printed the values within square brackets.
System.out.println(pair.toString("[%1$s, %2$s]")); //[Language, Java]
Pair equals method
The equals method of the Pair compares with another Pair instance by comparing the two values. It returns true if they are equal and false otherwise.
Let us create two pairs with same left and right values. Calling equals on them should return true.
Pair<String, String> pair1 = Pair.of(Map.entry("Language", "Java"));
Pair<String, String> pair2 = Pair.of(Map.entry("Language", "Java"));
System.out.println(pair1.equals(pair2)); //true
Let us create another Pair with a different value. Now, equals will return false.
pair2 = Pair.of(Map.entry("Language", "Advanced Java"));
System.out.println(pair1.equals(pair2)); //false
The same applies when we have custom class. Let us say we have a simple custom class called Model, as shown below.
public class Model {
private String name;
private Model(String name) {
this.name = name;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null|| obj.getClass()!=this.getClass() ) {
return false;
}
Model other = (Model) obj;
return name.equals(other.name);
}
}
Note that we have defined the equals and hashCode methods for it.
Now let us create two pairs of Integer and Model with same value and call equals on them.
Pair<Integer, Model> modelNumberPair1 = Pair.of(1, new Model("model-1"));
Pair<Integer, Model> modelNumberPair2 = Pair.of(1, new Model("model-1"));
System.out.println(modelNumberPair1.equals(modelNumberPair2)); //true
Since it just calls equals the key and value of the Pair, having equals implemented in the custom class is necessary.
Comparing two Pair objects
A Pair implements the Comparable interface and hence we can call compareTo on a Pair. It compares the pair based on the left element followed by the right element. The types must be comparable - otherwise it will throw a ClassCastException. It returns an integer:
- a negative value - if this pair is less than the other pair.
- zero - if both pairs are equal.
- a positive value - if this pair is greater than the other pair.
Let us look at examples for all scenarios.
Pair<Integer, Model> pair1 = Pair.of(Map.entry("Language", "Java"));
Pair<Integer, Model> pair2 = Pair.of(Map.entry("Language", "Java"));
System.out.println(pair1.compareTo(pair2)); //0
pair1 = Pair.of(Map.entry("Language", "Java"));
pair2 = Pair.of(Map.entry("Language", "Advanced Java"));
System.out.println(pair1.compareTo(pair2)); //9
pair1 = Pair.of(Map.entry("Language", "Java"));
pair2 = Pair.of(Map.entry("Level", "1"));
System.out.println(pair1.compareTo(pair2)); //-4
In the first case, the pairs are equal and hence it returns a 0. In the second case, the second pair’s value is less than the first pair’s value and hence it returns an integer greater than 0. Finally, in the third call, the second pair’s key is greater than the first pair’s and hence it returns an integer less than 0.
Note: Here, less/greater refers to the natural ordering of the String.
Let us look at examples for each scenario using the Model class. For this, we have to make the Model class implement the Comparable interface.
public class Model implements Comparable<Model> {
private String name;
private Model(String name) {
this.name = name;
}
//equals, hashCode methods
@Override
public int compareTo(Model o) {
return name.compareTo(o.name);
}
}
I’ll skip the explanation as it is very similar to the previous example.
Pair<Integer, Model> modelNumberPair1 = Pair.of(1, new Model("model-1"));
Pair<Integer, Model> modelNumberPair2 = Pair.of(1, new Model("model-1"));
System.out.println(modelNumberPair1.compareTo(modelNumberPair2)); //0
modelNumberPair1 = Pair.of(1, new Model("model-1"));
modelNumberPair2 = Pair.of(2, new Model("model-1"));
System.out.println(modelNumberPair1.compareTo(modelNumberPair2)); //-1
modelNumberPair1 = Pair.of(2, new Model("model-1"));
modelNumberPair2 = Pair.of(1, new Model("model-1"));
System.out.println(modelNumberPair1.compareTo(modelNumberPair2)); //1
modelNumberPair1 = Pair.of(1, new Model("model-1"));
modelNumberPair2 = Pair.of(1, new Model("model-4"));
System.out.println(modelNumberPair1.compareTo(modelNumberPair2)); //-3
Immutability of Pair
Note that a Pair created using the static factory methods of() in the Pair class returns an ImmutablePair. But since a Pair implements Map.Entry it would have the setValue method. But we cannot call setValue on an ImmutablePair. If we do, it will throw an UnsupportedOperationException.
Pair<String, String> pair = Pair.of("Language", "Java");
//throws java.lang.UnsupportedOperationException
pair.setValue("C");
Immutable Pair
We have already operated on an ImmutablePair before, but let us go over the ImmutablePair class as it has some additional methods.
ImmutablePair - Creation
We can call the static method of() to create an ImmutablePair. As before, we can pass the left and right values or can pass a Map.Entry instance.
ImmutablePair<String, String> immutablePair = ImmutablePair.of("Language", "Java");
System.out.println(immutablePair.getLeft()); //Language
System.out.println(immutablePair.getRight()); //Java
System.out.println(immutablePair.getKey()); //Language
System.out.println(immutablePair.getValue()); //Java
//Default toString representation
System.out.println(immutablePair);// (Language,Java)
Using a Map.Entry,
ImmutablePair<String, String>immutablePair = ImmutablePair.of(Map.entry("Language", "Java"));
System.out.println(immutablePair.getLeft()); //Language
System.out.println(immutablePair.getRight()); //Java
System.out.println(immutablePair.getKey()); //Language
System.out.println(immutablePair.getValue()); //Java
In addition, there is also a constructor which takes the Pair’s left and right values.
ImmutablePair<String, String> immutablePair = new ImmutablePair<>("Language", "Java");
ImmutablePair - toString
Since an ImmutablePair is a Pair (extends the Pair class we saw earlier), the toString method to which we passed a custom format is available on an ImmutablePair as well.
System.out.println(immutablePair.toString("[%1$s, %2$s]")); //[Language, Java]
ImmutablePair - nullPair
An ImmutablePair has a nullPair method which allows us to create an ImmutablePair whose left and right are null.
ImmutablePair<String, String> nullPair = ImmutablePair.nullPair();
System.out.println(nullPair.getLeft()); //null
System.out.println(nullPair.getRight()); //null
ImmutablePair - left and right methods
Since Apache Commons Lang 3.11, we have two new static methods on the ImmutablePair class - left() and right(). We can use them to create an ImmutablePair with only one value (either left or right).
Pair<String, String> pair = ImmutablePair.left("Language");
System.out.println(pair.getLeft()); //Language
System.out.println(pair.getRight()); //null
In the above code, we called the left() static method and passed the left value alone. Hence, the right value of the pair is null.
Similarly, we can use the right method to create an ImmutablePair with only the right value (and a null left value)
Pair<String, String> pair = ImmutablePair.right("Java");
System.out.println(pair.getLeft()); //null
System.out.println(pair.getRight()); //Java
At last, since an ImmutablePair is immutable, we cannot call setValue as seen before. It will throw an UnsupportedOperationException if we did.
Mutable Pair
A MutablePair is a Pair (extends the Pair class), but it is mutable i.e., we can update the left and the right values.
Creation of Mutable Pair
Similar to an ImmutablePair, we can create a MutablePair through
- the static factory methods or
- the constructor
Using the static factory method:
MutablePair<String, String> mutablePair = MutablePair.of("Language", "Java");
System.out.println(mutablePair.getLeft()); //Language
System.out.println(mutablePair.getRight()); //Java
System.out.println(mutablePair.getKey()); //Language
System.out.println(mutablePair.getValue()); //Java
System.out.println(mutablePair); //(Language,Java)
Using the static factory method (passing Map.Entry),
MutablePair<String, String> mutablePair = MutablePair.of(Map.entry("Language", "Java"));
System.out.println(mutablePair.getLeft()); //Language
System.out.println(mutablePair.getRight()); //Java
System.out.println(mutablePair.getKey()); //Language
System.out.println(mutablePair.getValue()); //Java
And by using the constructor, we can achieve the same result.
MutablePair<String, String> mutablePair = new MutablePair<>("Language", "Java");
Mutating the Pair
With a MutablePair, we can call the setValue method to update the value (the right).
MutablePair<String, String> mutablePair = MutablePair.of("Language", "Java");
mutablePair.setValue("C");
System.out.println(mutablePair); //(Language,C)
In the above example, we created a MutablePair with “Language” as the key and “Java” as the value. Then we updated the value to “C”.
As another example, let us create a MutablePair with null values. Then we will update the left using setLeft() method and the right using setRight() method.
MutablePair<String, String> mutablePair = new MutablePair<>();
//Updating left
mutablePair.setLeft("Language");
System.out.println(mutablePair); //(Language,null)
//Updating right
mutablePair.setRight("C");
System.out.println(mutablePair); //(Language,C)
//Updating right again using setValue
mutablePair.setValue("Java");
System.out.println(mutablePair); //(Language,Java)
As we have seen, the value or the right can be updated by either setRight() or the setValue() method. The setValue method is inherited from the Map.Entry interface.
toString
As seen before, we can pass a custom format string for printing the pair (this toString method is present in the Pair parent class).
System.out.println(mutablePair.toString("[%1$s, %2$s]")); //[Language, Java]
Conclusion
In this post, we have seen how to use the Apache Commons Lang Pair class. We use this class when we have a need to manage one or more pair of values (key-values).
References
Javadoc of