Apache Commons Lang Pair

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 on 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

Leave a Reply