DivideByHero
DivideByHero

Reputation: 20315

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!

Upvotes: 454

Views: 659804

Answers (23)

Philippe Cloutier
Philippe Cloutier

Reputation: 583

Record classes

Although you mention wanting to avoid the class route, if you want/accept entries to be immutable and use Java 16 or above, I strongly recommend considering creating a new class (or perhaps clarifying what concerns you see with that), now that Record classes and the record keyword make it so concise. With a short (although yes, the braces are needed) one-line declaration like:

record TestResult(String name, Integer score) {}

…you can create all you need to return a collection of typed ordered pairs:

List<TestResult> getTestResults() {
    ArrayList<TestResult> results = new ArrayList<>();
    results.add(new TestResult('Bart Simpson', 44);
    results.add(new TestResult('Lisa Simpson', 97);
    return results;
}

Other options

If you use Java 15 or below or need mutable entries, I defer to Hans Brende's answer.

Upvotes: 0

Hans Brende
Hans Brende

Reputation: 8445

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.


Hand-rolled implementations

As of Java 16, records have come out of preview status, so you can now do:

public record Pair<K, V>(K key, V value) {
    public static <K, V> Pair<K, V> of(K key, V value) {
        return new Pair<>(key, value);
    }
}

The above implementation will have a big advantage in the future, as it'll allow you to do record deconstruction.

Prior to Java 16, you can achieve the same semantics with Project Lombok:

@Value(staticConstructor = "of")
public class Pair<K, V> {
    K key;
    V value;
}

or, with the following verbosity (which, unlike the class listed in the accepted answer, guards against NullPointerExceptions, and has a robust hashCode() implementation identical to that of Records1):

import java.util.Objects;

public class Pair<K, V> {

    public final K key;
    public final V value;

    private Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public static <K, V> Pair<K, V> of(K key, V value) {
        return new Pair<>(key, 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;
    }
}

1 Tested on OpenJDK 17

Upvotes: 264

LordOfThePigs
LordOfThePigs

Reputation: 11300

What you want is a List of some kind of object. I personally do not like using generic Pair classes. They have too many disadvantages:

  • They are not very expressive. They provide no contextual information besides their type arguments.
  • They prevent you from using primitive types
  • You can't apply any constraints on keys or values (besides their type, of course).
  • They force you to carry the verbose type parameter declaration in many places (though this is somewhat mitigated by var and <>)

I prefer using ad-hoc classes. While a few years ago, this came with annoying boilerplate, this is no longer the case.

Here is a couple of alternatives that makes the declaration of the class really easy:

Java 14 and above: use a record class

record TypedCount(String type, int count) {}

There, that's it. You get toString() and equals() for free

Before Java 14: use lombok's @Value annotation

@Value
class TypedCount {
    String type;
    int count;
}

That's also it. Lombok automatically makes the fields private final, constructors, getters, toString() and equals().

While this requires you to add lombok as a dependency, this is only a compile-time dependency that generates the code for you. The lombok library does not need to be in your classpath at runtime.

Upvotes: 1

Aqeel Ashiq
Aqeel Ashiq

Reputation: 2185

You can reuse existing Pair or any such class from "God knows how many libraries already provide such classes". And If you do not want anything from Hans Brende's answer on this question then I don't see any reason for not using 2D Array or List of Object Arrays/ArrayLists used as Pairs/Tuples. The reason for not using Array, you mentioned:

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.

Even if you use Pair class from the Accepted Answer, you'll still have to cast the key and value objects. Since you want to store objects of all the types in there. In other words List<Pair> pairs is no different from List<Pair<? extends Object>> pairs which in turn is no different from Object[][2] or List<Object[]> or List<List<Object>>. Because if you write the following code:

List<Pair> pairs = new ArrayList<>();

// logic for populating pairs into list goes here

// then after sometime you need an element
Pair p = pairs.get(whateverIndex);

Object key = p.getKey(); // We don't know type of key, right?
Object value = p.getValue(); // We don't know the exact type here as well

// All sorts of type guessing statemntes go here
GuessedTypeOfKey finallyMyKey = (GuessedTypeOfKey) key;
GuessedTypeOfValue finallyMyValue = (GuessedTypeOfValue) value;

You still have to do the type casting. So I don't find any other reason to not use 2d Object array or List of Object Arrays/ArrayLists used as Pairs/Tuples . Following is code using List and arrays

List<Object[]> simplePairs = new ArrayList<>();

// Add values to pairs
simplePairs.add(new Object[]{1,"One"});
simplePairs.add(new Object[]{"Another Key of Different Type","Value"});
simplePairs.add(new Object[]{"Another Key of Different Type",new AnotherType("Another Value Type")});

// get values
Object[] pair = simplePairs.get(whateverIndex);
Object key = pair[0];
Object value = pair[1];

Upvotes: 0

user93
user93

Reputation: 1866

just create a class like

class Tuples 
{ 
    int x;
    int y;
} 

then create List of this objects of Tuples

List<Tuples> list = new ArrayList<>();

so you can also implement other new data structures in the same way.

Upvotes: 3

deamon
deamon

Reputation: 92357

Java 14+ edition

You can create a record which implements equals, hashCode, and toString out of the box. Interfaces like Comparable could also be implemented, if needed.

record Pair<A, B>(A first, B second) {}

Records are immutable.

Upvotes: 12

Paul Brinkley
Paul Brinkley

Reputation: 6353

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.)

Upvotes: 285

muttonUp
muttonUp

Reputation: 6707

Spring has a Pair<S,T> type in the Data Utils package org.springframework.data.util

Pair<String,Integer> pair = Pair.of("Test", 123);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());

Upvotes: 6

Tires
Tires

Reputation: 1602

In project Reactor (io.projectreactor:reactor-core) there is advanced support for n-Tuples:

Tuple2<String, Integer> t = Tuples.of("string", 1)

There you can get t.getT1(), t.getT2(), ... Especially with Stream or Flux you can even map the tuple elements:

Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));

Upvotes: 3

Johannes Weiss
Johannes Weiss

Reputation: 54001

Map.Entry

These built-in classes are an option, too. Both implement the Map.Entry interface.

UML diagram of Map.Entry interface with pair of implementing classes

Upvotes: 76

muka90
muka90

Reputation: 89

First Thing on my mind when talking about key/value pairs is the Properties Class where you can save and load items to a stream/file.

Upvotes: -1

JavaHelp4u
JavaHelp4u

Reputation: 3607

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);

Upvotes: 356

changed
changed

Reputation: 2143

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());
}

Upvotes: 33

SomeDude
SomeDude

Reputation: 49

I mean, even though there is no Pair class in Java there is something pretty simmilar: Map.Entry

Map.Entry Documentation

This is (simplifying quite a bit) what HashMap , or actually any Map stores.

You can create an instance of Map store your values in it and get the entry set. You will end up with a Set<Map.Entry<K,V>> which effectively is what you want.

So:

public static void main(String []args)
{    
    HashMap<String, Integer> values = new HashMap<String,Integer>();
    values.put("A", 235);//your custom data, the types may be different
    //more data insertions....
    Set<Map.Entry<String,Integer>> list = values.entrySet();//your list 
    //do as you may with it
}

Upvotes: 4

danidemi
danidemi

Reputation: 4696

What about "Apache Commons Lang 3" Pair class and the relative subclasses ?

    import org.apache.commons.lang3.tuple.ImmutablePair;
    import org.apache.commons.lang3.tuple.Pair;
    ...
    @SuppressWarnings("unchecked")
    Pair<String, Integer>[] arr = new ImmutablePair[]{
            ImmutablePair.of("A", 1),
            ImmutablePair.of("B", 2)};

    // both access the 'left' part
    String key = arr[0].getKey();
    String left = arr[0].getLeft();

    // both access the 'right' part
    Integer value = arr[0].getValue();
    Integer right = arr[0].getRight();

ImmutablePair is a specific subclass that does not allow the values in the pair to be modified, but there are others implementations with different semantic. These are the Maven coordinates, if you need them.

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>

Upvotes: 16

Leo Ufimtsev
Leo Ufimtsev

Reputation: 6492

This is based on JavaHelp4u 's code.

Less verbose and shows how to do in one line and how to loop over things.

//======>  Imports
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

//======>  Single Entry
SimpleEntry<String, String> myEntry = new SimpleEntry<String, String>("ID", "Text");
System.out.println("key: " + myEntry.getKey() + "    value:" + myEntry.getValue());
System.out.println();

//======>  List of Entries
List<Entry<String,String>> pairList = new ArrayList<>();

//-- Specify manually
Entry<String,String> firstButton = new SimpleEntry<String, String>("Red ", "Way out");
pairList.add(firstButton);

//-- one liner:
pairList.add(new SimpleEntry<String,String>("Gray", "Alternate route"));  //Ananomous add.

//-- Iterate over Entry array:
for (Entry<String, String> entr : pairList) {
    System.out.println("Button: " + entr.getKey() + "    Label: " + entr.getValue());
}

Upvotes: 4

To anyone developing for Android, you can use android.util.Pair. :)

Upvotes: 19

Bobak_KS
Bobak_KS

Reputation: 554

Apache Crunch also has a Pair class: http://crunch.apache.org/apidocs/0.5.0/org/apache/crunch/Pair.html

Upvotes: 2

simbo1905
simbo1905

Reputation: 6822

Expanding on the other answers a generic immutable Pair should have a static method to avoid cluttering your code with the call to the constructor:

class Pair<L,R> {
      final L left;
      final R right;

      public Pair(L left, R right) {
        this.left = left;
        this.right = right;
      }

      static <L,R> Pair<L,R> of(L left, R right){
          return new Pair<L,R>(left, right);
      }
}

if you name the static method "of" or "pairOf" the code becomes fluent as you can write either:

    list.add(Pair.of(x,y)); // my preference
    list.add(pairOf(x,y)); // use with import static x.y.Pair.pairOf

its a real shame that the core java libraries are so sparse on such things that you have to use commons-lang or other 3rd parties to do such basic stuff. yet another reason to upgrade to scala...

Upvotes: 7

Rene H.
Rene H.

Reputation: 17

What about com.sun.tools.javac.util.Pair?

Upvotes: -1

Jeremy Rishel
Jeremy Rishel

Reputation: 239

The preferred solution as you've described it is a List of Pairs (i.e. List).

To accomplish this you would create a Pair class for use in your collection. This is a useful utility class to add to your code base.

The closest class in the Sun JDK providing functionality similar to a typical Pair class is AbstractMap.SimpleEntry. You could use this class rather than creating your own Pair class, though you would have to live with some awkward restrictions and I think most people would frown on this as not really the intended role of SimpleEntry. For example SimpleEntry has no "setKey()" method and no default constructor, so you may find it too limiting.

Bear in mind that Collections are designed to contain elements of a single type. Related utility interfaces such as Map are not actually Collections (i.e. Map does not implement the Collection interface). A Pair would not implement the Collection interface either but is obviously a useful class in building larger data structures.

Upvotes: 6

JMD
JMD

Reputation: 7377

I was going to ask if you would not want to just use a List<Pair<T, U>>? but then, of course, the JDK doesn't have a Pair<> class. But a quick Google found one on both Wikipedia, and forums.sun.com. Cheers

Upvotes: 4

Dan Dyer
Dan Dyer

Reputation: 54465

You could write a generic Pair<A, B> class and use this in an array or list. Yes, you have to write a class, but you can reuse the same class for all types, so you only have to do it once.

Upvotes: 10

Related Questions