A Java collection of value pairs? (tuples?)
I like how Java has a Map where you can define the types of each entry in the map, for example <String, Integer>
.
What I'm looking for is a type of collection where each element in the collection is a pair of values. Each value in the pair can have its own type (like the String and Integer example above), which is defined at declaration time.
The collection will maintain its given order and will not treat one of the values as a unique key (as in a map).
Essentially I want to be able to define an ARRAY of type <String,Integer>
or any other 2 types.
I realize that I can make a class with nothing but the 2 variables in it, but that seems overly verbose.
I also realize that I could use a 2D array, but because of the different types I need to use, I'd have to make them arrays of OBJECT, and then I'd have to cast all the time.
I only need to store pairs in the collection, so I only need two values per entry. Does something like this exist without going the class route? Thanks!
AbstractMap.SimpleEntry
Easy you are looking for this:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
How can you fill it?
java.util.Map.Entry<String,Integer> pair1=new java.util.AbstractMap.SimpleEntry<>("Not Unique key1",1);
java.util.Map.Entry<String,Integer> pair2=new java.util.AbstractMap.SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);
This simplifies to:
Entry<String,Integer> pair1=new SimpleEntry<>("Not Unique key1",1);
Entry<String,Integer> pair2=new SimpleEntry<>("Not Unique key2",2);
pairList.add(pair1);
pairList.add(pair2);
And, with the help of a createEntry
method, can further reduce the verbosity to:
pairList.add(createEntry("Not Unique key1", 1));
pairList.add(createEntry("Not Unique key2", 2));
Since ArrayList
isn't final, it can be subclassed to expose an of
method (and the aforementioned createEntry
method), resulting in the syntactically terse:
TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);
The Pair class is one of those "gimme" generics examples that is easy enough to write on your own. For example, off the top of my head:
public class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
assert left != null;
assert right != null;
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
@Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
@Override
public boolean equals(Object o) {
if (!(o instanceof Pair)) return false;
Pair pairo = (Pair) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
}
}
And yes, this exists in multiple places on the Net, with varying degrees of completeness and feature. (My example above is intended to be immutable.)
Java 9+
In Java 9, you can simply write: Map.entry(key, value)
to create an immutable pair.
Note: this method does not allow keys or values to be null. If you want to allow null values, for example, you'd want to change this to: Map.entry(key, Optional.ofNullable(value))
.
Java 8+
In Java 8, you can use the more general-purpose javafx.util.Pair
to create an immutable, serializable pair. This class does allow null keys and null values. (In Java 9, this class is included in the javafx.base
module). EDIT: As of Java 11, JavaFX has been decoupled from the JDK, so you'd need the additional maven artifact org.openjfx:javafx-base.
Java 6+
In Java 6 and up, you can use the more verbose AbstractMap.SimpleImmutableEntry
for an immutable pair, or AbstractMap.SimpleEntry
for a pair whose value can be changed. These classes also allow null keys and null values, and are serializable.
Android
If you're writing for Android, just use Pair.create(key, value)
to create an immutable pair.
Apache Commons
Apache Commons Lang
provides the helpful Pair.of(key, value)
to create an immutable, comparable, serializable pair.
Eclipse Collections
If you're using pairs that contain primitives, Eclipse Collections provides some very efficient primitive pair classes that will avoid all the inefficient auto-boxing and auto-unboxing.
For instance, you could use PrimitiveTuples.pair(int, int)
to create an IntIntPair
, or PrimitiveTuples.pair(float, long)
to create a FloatLongPair
.
Project Lombok
Using Project Lombok, you can create an immutable pair class simply by writing:
@Value
public class Pair<K, V> {
K key;
V value;
}
Lombok will fill in the constructor, getters, equals()
, hashCode()
, and toString()
methods for you automatically in the generated bytecode. If you want a static factory method instead of a constructor, e.g., a Pair.of(k, v)
, simply change the annotation to: @Value(staticConstructor = "of")
.
Otherwise
If none of the above solutions float your boat, you can simply copy and paste the following code (which, unlike the class listed in the accepted answer, guards against NullPointerExceptions):
import java.util.Objects;
public class Pair<K, V> {
public final K key;
public final V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public boolean equals(Object o) {
return o instanceof Pair && Objects.equals(key, ((Pair<?,?>)o).key) && Objects.equals(value, ((Pair<?,?>)o).value);
}
public int hashCode() {
return 31 * Objects.hashCode(key) + Objects.hashCode(value);
}
public String toString() {
return key + "=" + value;
}
}
Map.Entry
These built-in classes are an option, too. Both implement the Map.Entry
interface.
AbstractMap.SimpleEntry
-
AbstractMap.SimpleImmutableEntry
Apache common lang3 has Pair class and few other libraries mentioned in this thread What is the equivalent of the C++ Pair<L,R> in Java?
Example matching the requirement from your original question:
List<Pair<String, Integer>> myPairs = new ArrayList<Pair<String, Integer>>();
myPairs.add(Pair.of("val1", 11));
myPairs.add(Pair.of("val2", 17));
//...
for(Pair<String, Integer> pair : myPairs) {
//following two lines are equivalent... whichever is easier for you...
System.out.println(pair.getLeft() + ": " + pair.getRight());
System.out.println(pair.getKey() + ": " + pair.getValue());
}