Une collection Java de paires de valeurs? (tuples?)

345

J'aime la façon dont Java a une carte où vous pouvez définir les types de chaque entrée dans la carte, par exemple <String, Integer>.

Ce que je recherche, c'est un type de collection où chaque élément de la collection est une paire de valeurs. Chaque valeur de la paire peut avoir son propre type (comme l'exemple String et Integer ci-dessus), qui est défini au moment de la déclaration.

La collection conservera son ordre donné et ne traitera pas l'une des valeurs comme une clé unique (comme dans une carte).

Essentiellement, je veux pouvoir définir un ARRAY de type <String,Integer>ou tout autre 2 types.

Je me rends compte que je peux faire une classe avec rien que les 2 variables en elle, mais cela semble trop verbeux.

Je me rends également compte que je pourrais utiliser un tableau 2D, mais en raison des différents types que je dois utiliser, je devrais en faire des tableaux OBJECT, puis je devrais lancer tout le temps.

J'ai seulement besoin de stocker des paires dans la collection, donc je n'ai besoin que de deux valeurs par entrée. Est-ce que quelque chose comme ça existe sans suivre l'itinéraire de classe? Merci!

DivideByHero
la source
Je me demande si Guava pourrait avoir une classe pour ça aussi.
Sikorski
La goyave est assez anti- Pair, et les gens de Google sont allés jusqu'à créer une bien meilleure alternative - Auto / Value . Il vous permet de créer facilement des classes de types de valeurs bien typées , avec la sémantique equals / hashCode appropriée. Vous n'aurez plus jamais besoin d'un Pairtype!
dimo414

Réponses:

255

La classe Pair est l'un de ces exemples génériques "gimme" qui est assez facile à écrire par vous-même. Par exemple, du haut de ma tête:

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

}

Et oui, cela existe à plusieurs endroits sur le Net, avec différents degrés d'exhaustivité et de fonctionnalité. (Mon exemple ci-dessus est destiné à être immuable.)

Paul Brinkley
la source
17
J'aime ça, mais que pensez-vous de rendre publics les champs gauche et droit? Il est assez clair que la classe Pair n'aura jamais de logique associée et que tous les clients auront besoin d'accéder à «gauche» et «droite», alors pourquoi ne pas le rendre facile?
Outlaw Programmer
43
Hum ... non ça ne le serait pas. Les champs sont marqués comme définitifs, ils ne peuvent donc pas être réaffectés. Et ce n'est pas threadsafe parce que «gauche» et «droite» pourraient être mutables. À moins que getLeft () / getRight () ne retourne des copies défensives (inutiles dans ce cas), je ne vois pas ce qui est important.
Outlaw Programmer
17
Notez que hashCode () tel quel donne la même valeur si gauche et droite sont échangées. Peut-être:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze
68
Donc, si je comprends bien, le déploiement d'une classe de paire simple génère une erreur de syntaxe, une méthode de hachage Subpar, des exceptions de pointeur nul, pas de méthode compareTo, des questions de conception ... et les gens préconisent toujours de déployer cette classe alors qu'elle existe dans Apache commons. S'il vous plaît, copiez simplement le code si vous ne voulez pas inclure le JAR mais arrêtez de réinventer la roue!
cquezel
38
Que voulez-vous dire par "assez facile à écrire par vous-même"? C'est terrible génie logiciel. Les classes d'adaptateurs N ^ 2 à convertir entre MyPair et SomeoneElsesPair sont-elles également considérées comme assez faciles à écrire par vous-même?
djechlin
299

AbstractMap.SimpleEntry

Facile, vous recherchez ceci:

java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();

Comment pouvez-vous le remplir?

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

Cela simplifie:

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

Et, à l'aide d'une createEntryméthode, peut encore réduire la verbosité à:

pairList.add(createEntry("Not Unique key1", 1));
pairList.add(createEntry("Not Unique key2", 2));

Étant donné qu'il ArrayListn'est pas définitif, il peut être sous-classé pour exposer une ofméthode (et la createEntryméthode susmentionnée ), ce qui entraîne la syntaxe laconique:

TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);
JavaHelp4u
la source
11
FYI: Cela a suggéré SimpleEntryune setValueméthode omettant la classe frère pour être immuable. D'où le nom SimpleImmutableEntry.
Basil Bourque
3
C'est mieux que l'auto-implémentation. Le fait qu'il soit facile à implémenter n'est pas une excuse pour l'implémenter à chaque fois.
pevogam
6
Je ne le considérerais pas comme "standard". Entryest censé être une paire valeur / clé, et c'est très bien pour cela. Mais il a des faiblesses comme un vrai tuple. Par exemple, l' hashcodeimplémentation en SimpleEntryxors juste les codes des éléments, donc <a,b>hache à la même valeur que <b,a>. Les implémentations de tuple trient souvent lexicographiquement, mais SimpleEntry ne l'implémentent pas Comparable. Alors faites attention là-bas ...
Gene
167

Java 9+

En Java 9, vous pouvez simplement écrire: Map.entry(key, value) pour créer une paire immuable.

Remarque: cette méthode ne permet pas que les clés ou les valeurs soient nulles. Si vous souhaitez autoriser les valeurs NULL, par exemple, vous voulez changer cela: Map.entry(key, Optional.ofNullable(value)).


Java 8+

Dans Java 8, vous pouvez utiliser le plus polyvalent javafx.util.Pairpour créer une paire immuable et sérialisable. Cette classe ne permet touches nulles et les valeurs NULL. (En Java 9, cette classe est incluse dans le javafx.basemodule). EDIT: Depuis Java 11, JavaFX a été découplé du JDK, vous auriez donc besoin de l'artefact maven supplémentaire org.openjfx: javafx-base.


Java 6+

Dans Java 6 et versions ultérieures, vous pouvez utiliser le plus détaillé AbstractMap.SimpleImmutableEntrypour une paire immuable ou AbstractMap.SimpleEntrypour une paire dont la valeur peut être modifiée. Ces classes autorisent également les clés et les valeurs nulles et sont sérialisables.


Android

Si vous écrivez pour Android, utilisez simplement Pair.create(key, value)pour créer une paire immuable.


Apache Commons

Apache Commons Langfournit l'utile Pair.of(key, value)pour créer une paire immuable, comparable et sérialisable.


Collections Eclipse

Si vous utilisez des paires contenant des primitives, Eclipse Collections fournit des classes de paires de primitives très efficaces qui éviteront toutes les boîtes automatiques et décompresseurs automatiques inefficaces.

Par exemple, vous pouvez utiliser PrimitiveTuples.pair(int, int)pour créer un IntIntPairou PrimitiveTuples.pair(float, long)pour créer un FloatLongPair.


Projet Lombok

En utilisant Project Lombok , vous pouvez créer une classe de paires immuable simplement en écrivant:

@Value
public class Pair<K, V> {
    K key;
    V value;
}

Lombok remplira le constructeur, apporteurs, equals(), hashCode()et les toString()méthodes pour vous automatiquement dans le bytecode généré. Si vous voulez une méthode usine statique au lieu d'un constructeur, par exemple, un Pair.of(k, v), modifiez simplement l'annotation à: @Value(staticConstructor = "of").


Autrement

Si aucune des solutions ci-dessus ne fait flotter votre bateau, vous pouvez simplement copier et coller le code suivant (qui, contrairement à la classe répertoriée dans la réponse acceptée, protège contre 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;
    }
}
Hans Brende
la source
3
JavaFX est cependant uniquement de bureau, ajoutant une dépendance inutile pour les environnements non-bureau (par exemple les serveurs).
foo
2
Eclipse Collections possède également une Pairinterface pour les objets, qui peut être instanciée en appelant Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
Donald Raab
2
Cela ressemble à la réponse la plus véritablement Java. TOUTES les implémentations disponibles. Très complet, merci beaucoup!
Mike
Salut @Hans J'utilise une liste List<Pair<Integer, String> listOfTuple. Comment dois-je l'utiliser listOfTuple.add()? Si je fais - listOfTuple.add(Pair<someInteger, someString>);, cela ne fonctionnera pas. Merci d'avance.
Me_developer
la version android n'est pas disponible en test unitaire, ce qui la rend pas vraiment utile ...
OznOg
64

Map.Entry

Ces classes intégrées sont également une option. Les deux implémentent l' Map.Entryinterface.

Diagramme UML de l'interface Map.Entry avec paire de classes d'implémentation

Johannes Weiss
la source
10
Ce n'est pas seulement une option, c'est la bonne réponse. Je pense que certaines personnes préfèrent juste réinventer la roue.
CurtainDog
31

Apache common lang3 a la classe Pair et quelques autres bibliothèques mentionnées dans ce fil Quel est l'équivalent de la paire C ++ <L, R> en Java?

Exemple correspondant à l'exigence de votre question d'origine:

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());
}
modifié
la source
1
C'est l'option la plus simple si Apache Commons est disponible.
Kip
18

Pour tous ceux qui développent pour Android, vous pouvez utiliser android.util.Pair . :)

XåpplI'-I0llwlg'I -
la source
15

Qu'en est-il de la Pairclasse "Apache Commons Lang 3" et des sous-classes relatives?

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

ImmutablePairest une sous-classe spécifique qui ne permet pas de modifier les valeurs de la paire, mais il existe d'autres implémentations avec une sémantique différente. Ce sont les coordonnées Maven, si vous en avez besoin.

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
danidemi
la source
11

Vous pouvez écrire une classe Pair <A, B> générique et l'utiliser dans un tableau ou une liste. Oui, vous devez écrire une classe, mais vous pouvez réutiliser la même classe pour tous les types, vous n'avez donc à le faire qu'une seule fois.

Dan Dyer
la source
J'adorerais voir un exemple de ça!
DivideByHero
1
Dan, la mauvaise chose à ce sujet est qu'il n'est pas possible d'accepter, par exemple, uniquement des paires <chaîne, entier> à cause de l'effacement de type, non? Mais Java aussi ...
Johannes Weiss
Je ne pense pas que cela fonctionnera pour les tableaux simples, mais cela fonctionnera certainement pour d'autres collections.
Outlaw Programmer
@Outlaw Programmer: Bon point, vous pouvez l'utiliser avec des tableaux, mais c'est moche car vous devez utiliser un hack pour créer des tableaux génériques.
Dan Dyer
7

En développant les autres réponses, une paire générique immuable devrait avoir une méthode statique pour éviter d'encombrer votre code avec l'appel au constructeur:

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

si vous nommez la méthode statique "of" ou "pairOf", le code devient fluide car vous pouvez écrire soit:

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

c'est vraiment dommage que les bibliothèques java de base soient si rares sur de telles choses que vous devez utiliser commons-lang ou d'autres tierces parties pour faire de telles choses de base. encore une autre raison de passer à scala ...

simbo1905
la source
6

J'allais vous demander si vous ne voudriez pas simplement utiliser un List<Pair<T, U>>? mais alors, bien sûr, le JDK n'a pas de classe Pair <>. Mais un rapide Google en a trouvé un sur Wikipédia et sur forums.sun.com . À votre santé

JMD
la source
6

La solution préférée telle que vous l'avez décrite est une liste de paires (c'est-à-dire une liste).

Pour ce faire, vous devez créer une classe Pair à utiliser dans votre collection. Il s'agit d'une classe utilitaire utile à ajouter à votre base de code.

AbstractMap.SimpleEntry est la classe la plus proche dans le JDK Sun fournissant des fonctionnalités similaires à une classe Pair typique. Vous pouvez utiliser cette classe plutôt que de créer votre propre classe Pair, bien que vous deviez vivre avec des restrictions maladroites et je pense que la plupart des gens ne verraient pas cela comme le rôle prévu de SimpleEntry. Par exemple, SimpleEntry n'a pas de méthode "setKey ()" et aucun constructeur par défaut, vous pouvez donc la trouver trop limitative.

Gardez à l'esprit que les collections sont conçues pour contenir des éléments d'un seul type. Les interfaces utilitaires associées telles que Map ne sont pas réellement des collections (c'est-à-dire que Map n'implémente pas l'interface Collection). Une paire n'implémenterait pas non plus l'interface de collection, mais est évidemment une classe utile pour construire des structures de données plus grandes.

Jeremy Rishel
la source
Je trouve cette solution bien meilleure que celle "gagnante" avec la paire générique <K, V>. Il fait tout ce qui est demandé et sort de la boîte. J'utilise celui-ci pour ma mise en œuvre.
Sauer
5

Ceci est basé sur le code de JavaHelp4u.

Moins verbeux et montre comment faire en une seule ligne et comment boucler les choses.

//======>  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());
}
Leo Ufimtsev
la source
4

il suffit de créer une classe comme

class tuples 
{ 
int x;
int y;
} 

puis créez la liste de ces objets de tuples

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

afin que vous puissiez également implémenter d'autres nouvelles structures de données de la même manière.

user93
la source
4

Je veux dire, même s'il n'y a pas de Pairclasse en Java, il y a quelque chose de très similaire:Map.Entry

Documentation Map.Entry

C'est (simplifiant un peu) quoi HashMap, ou en fait n'importe quel Mapmagasin.

Vous pouvez créer une instance pour y Mapstocker vos valeurs et obtenir le jeu d'entrées. Vous vous retrouverez avec un Set<Map.Entry<K,V>>qui est effectivement ce que vous voulez.

Donc:

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
}
SomeDude
la source
Cette option a le problème des clés uniques. Disons que vous avez des attributs de personnes (par exemple {(personne1, "aux yeux bleus"), (personne1, "rousse", (personne2, "myope"), (personne2, "penseur rapide")}) vous le feriez ne pas pouvoir les stocker par paires dans une carte, car chaque personne est la clé de la carte et n'autoriserait qu'un seul attribut par personne.
manuelvigarcia
2

Spring a un Pair<S,T>type dans le package Data Utilsorg.springframework.data.util

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

Qu'en est-il de com.sun.tools.javac.util.Pair?

René H.
la source
7
Ce type se trouve dans tools.jar - une partie de l'implémentation "Sun" du compilateur javac. Il est uniquement distribué dans le cadre du JDK, peut ne pas être présent dans les implémentations d'autres fournisseurs et ne fera probablement pas partie de l'API publique.
McDowell
0

La première chose qui me vient à l'esprit lorsque l'on parle de paires clé / valeur est la classe Propriétés où vous pouvez enregistrer et charger des éléments dans un flux / fichier.

muka90
la source
0

Dans le projet Reactor (io.projectreactor: reactor-core), il existe un support avancé pour n-Tuples:

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

Là, vous pouvez obtenir t.getT1(), t.getT2(), ...Surtout avec Stream ou Flux, vous pouvez même mapper les éléments de tuple:

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