ChatGPT解决这个技术问题 Extra ChatGPT

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!

i wonder Guava might have a class for this also.
Guava is pretty anti-Pair, and the folks at Google have gone so far as to create a much better alternative - Auto/Value. It lets you easily create well-typed value type classes, with proper equals/hashCode semantics. You'll never need a Pair type again!
I would have loved for the answers to go a little further and went into the subject of generic n-tuples, not just pairs, but tuples of arity n.

D
Dave Jarvis

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

FYI: That suggested SimpleEntry has a sibling class omitting setValue method to be immutable. Thus the name SimpleImmutableEntry.
This is better than self-implementation. The fact that it's easy to implement is not an excuse for implementing it each time.
I wouldn't consider it "standard." Entry is meant to be a key-value pair, and it's fine for that. But it has weaknesses as a true tuple. For example the hashcode implementation in SimpleEntry just xors the codes of the elements, so <a,b> hashes to the same value as <b,a>. Tuple implementations often sort lexicographically, but SimpleEntry doesn't implement Comparable. So be careful out there...
Thanks to @Gene for raising the hashcode issue, which came as a surprise to me. The use of XOR also means a bunch of other collisions, including that all pairs where key == value will produce the same (zero) hashcode.
D
Dave Jarvis

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


I like this, but what do you think about making the left and right fields public? It's pretty clear that the Pair class is never going to have any logic associated and all clients will need access to 'left' and 'right,' so why not make it easy?
Um...no it wouldn't. The fields are marked as final, so they can't be reassigned. And it's not threadsafe because 'left' and 'right' could be mutable. Unless getLeft()/getRight() returned defensive copies (useless in this case), I don't see what the big deal is.
Note that hashCode() as-is does gives the same value if left and right are swapped. Perhaps: long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
So if I understand correctly, rolling out a simple pair class yields a syntax error, a subpar hashCode method, null pointer exceptions, no compareTo method, design questions ... and people still advocate rolling out this class while it exists in Apache commons. Please, just copy the code if you don't want to include the JAR but stop reinventing the wheel!
What do you mean "easy enough to write on your own"? That's terrible software engineering. Are the N^2 adapter classes to convert between MyPair and SomeoneElsesPair also considered easy enough to write on one's own?
H
Hans Brende

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


JavaFX however is desktop-only, adding an unnecessary dependency for non-desktop environments (e.g. servers).
Eclipse Collections also has a Pair interface for objects, which can be instantiated by calling Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
Hi @Hans I am using a list List<Pair<Integer, String> listOfTuple. How do I use listOfTuple.add()? If I'll do - listOfTuple.add(Pair<someInteger, someString>);, this will not work. Thanks in advance.
the android version is not available in unit test, which makes it not really useful...
Best answer. It's a pity that the JDK doesn't include classes like Pair or Tuple, given that the language itself doesn't allow these constructions. Maybe the JDK authors don't want people to use Tuples just to avoid writing more meaningful classes.
B
Basil Bourque

Map.Entry

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

AbstractMap.SimpleEntry

AbstractMap.SimpleImmutableEntry

https://i.stack.imgur.com/r72Q0.png


C
Community

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

This is the simplest option if Apache Commons is available.
X
XåpplI'-I0llwlg'I -

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


d
danidemi

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>

D
Dan Dyer

You could write a generic Pair 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.


I'd love to see an example of that!
Dan, the bad thing about that is that it is not possible to accept e.g. only Pairs because of type erasure, no? But so is Java...
I don't think this will work for plain arrays, but it definitely will work for other Collections.
@Outlaw Programmer: Good point, you can use it with arrays, but it's ugly as you have to use a hack to create generic arrays.
s
simbo1905

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


J
Jeremy Rishel

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.


I find this solution way better than the "winning" one with the generic Pair. It does everything requested and comes out of the box. I use this one for my implementation.
d
deamon

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.


J
JMD

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


L
Leo Ufimtsev

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

m
muttonUp

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

B
Bobak_KS

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


Apache Crunch is retired as a project. And besides that it wouldn't be a good idea to pull in a big framework just to have a simple type like this.
S
SomeDude

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
}

This option has the problem of the unique keys. Let's say you have attributes of persons (e.g. {(person1, "blue-eyed"), (person1, "red-haired", (person2, "shortsighted"), (person2, "quick-thinker")}) you would not be able to store them as pairs in a map, since each person is the key to the map and would only allow one attribute per person.
M
Mukit09

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.


R
Rene H.

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


This type is in tools.jar - part of the "Sun" implementation of the javac compiler. It is only distributed as part of the JDK, may not be present in other vendors implementations and is unlikely to be part of the public API.
m
muka90

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.


T
Tires

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

S
Syed Aqeel Ashiq

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