Quel est l'équivalent de la paire C ++ <L, R> en Java?

671

Y a-t-il une bonne raison pour laquelle il n'y Pair<L,R>en a pas en Java? Quel serait l'équivalent de cette construction C ++? Je préfère éviter de réimplémenter le mien.

Il semble que 1.6 fournit quelque chose de similaire ( AbstractMap.SimpleEntry<K,V>), mais cela semble assez compliqué.

David Segonds
la source
7
Pourquoi est AbstractMap.SimpleEntryalambiqué?
CurtainDog
27
En raison du namig, nommer arbitrairement une clé et une valeur.
Enerccio du
2
@sffc JavaFX ne figure sur aucun des chemins de classe par défaut dans JDK7, son utilisation nécessite l'ajout manuel des bibliothèques d'exécution JFX.
Cord Rehn
3
@Enerccio: Donc, vous déclarez en fait que "premier" et "second" ne sont pas arbitraires, tandis que "clé" et "valeur" - l'est? C'est donc une bonne raison de ne pas avoir une telle classe dans le SDK. Il y aurait un différend éternel sur la dénomination «correcte».
fdreger

Réponses:

400

Dans un fil de discussioncomp.lang.java.help , Hunter Gratzner donne quelques arguments contre la présence d'une Pairconstruction en Java. L'argument principal est qu'une classe Pairne transmet aucune sémantique sur la relation entre les deux valeurs (comment savez-vous ce que "premier" et "deuxième" signifient?).

Une meilleure pratique consiste à écrire une classe très simple, comme celle proposée par Mike, pour chaque application que vous auriez faite de la Pairclasse. Map.Entryest un exemple d'une paire qui porte sa signification dans son nom.

Pour résumer, à mon avis, il vaut mieux avoir une classe Position(x,y), une classe Range(begin,end)et une classe Entry(key,value)plutôt qu'un générique Pair(first,second)qui ne me dit rien de ce qu'il est censé faire.

Luc Touraille
la source
143
Gratzner fend des poils. Nous sommes très heureux de retourner une seule valeur en tant que classe primitive ou intégrée sans l'encapsuler dans une classe. Si nous devions renvoyer un tuple d'une douzaine d'éléments, personne ne serait en désaccord, il devrait avoir sa propre classe. Quelque part au milieu se trouve une ligne de division (floue). Je pense que nos cerveaux de lézards peuvent faire face aux paires assez facilement.
Ian
25
Je suis d'accord avec Ian. Java vous permet de retourner int; cela ne vous oblige pas à créer un alias pour int à chaque fois que vous en utilisez un. Les paires ne sont pas très différentes.
Clément
5
Si nous pouvions décompresser une paire directement dans vos variables locales ou la transmettre à une méthode qui prend deux arguments, Pair serait une classe utile. Comme nous ne pouvons pas le décompresser comme ça, créer une classe significative et garder les valeurs ensemble ne semble pas trop mal. Et, si vous voulez vraiment une paire malgré les limitations, il y a toujours Object [2] + cast :-)
marcus
Le fait est que si vous n'êtes pas d'accord avec Gratzner, il y a des implémentations Pair à plusieurs endroits. Apache Commons et Guava l'ont tous deux IIRC. Utilisez-les. Mais mettre quelque chose dans les principales bibliothèques Java signifie que c'est une façon noble et approuvée de faire les choses (avec une majuscule) et comme les gens ne sont pas d'accord, nous ne devrions pas le mettre là. Il y a suffisamment de cruches dans les vieilles bibliothèques telles quelles, n'en mettons pas inutilement plus.
Haakon Løtveit
1
@Dragas Quand j'ai besoin d'une paire de valeurs alors ce n'est pas Java ... sérieusement?
idclev 463035818
156

C'est Java. Vous devez créer votre propre classe Pair personnalisée avec des noms de classe et de champ descriptifs, sans oublier que vous réinventerez la roue en écrivant hashCode () / equals () ou en implémentant Comparable encore et encore.

Andreas Krey
la source
61
Cela ne répond pas à la question "pourquoi". (Sauf si vous considérez que `` c'est Java '' une réponse)
Nikita Rybak
127
+1 pour se moquer de la verbosité de Java. -1 pour ne pas avoir répondu à la question.
Bennett McElwee
19
La moquerie Java aurait été bien si vous aviez pointé le langage Apache Commong, qui contient une classe Pair.
haylem
6
Ou vous pouvez simplement utiliserSimpleImmutableEntry
CurtainDog
33
La première phrase répond à la question "pourquoi?". C'est Java, et c'est tout.
masterziv
103

Classe de paire compatible HashMap:

public class Pair<A, B> {
    private A first;
    private B second;

    public Pair(A first, B second) {
        super();
        this.first = first;
        this.second = second;
    }

    public int hashCode() {
        int hashFirst = first != null ? first.hashCode() : 0;
        int hashSecond = second != null ? second.hashCode() : 0;

        return (hashFirst + hashSecond) * hashSecond + hashFirst;
    }

    public boolean equals(Object other) {
        if (other instanceof Pair) {
            Pair otherPair = (Pair) other;
            return 
            ((  this.first == otherPair.first ||
                ( this.first != null && otherPair.first != null &&
                  this.first.equals(otherPair.first))) &&
             (  this.second == otherPair.second ||
                ( this.second != null && otherPair.second != null &&
                  this.second.equals(otherPair.second))) );
        }

        return false;
    }

    public String toString()
    { 
           return "(" + first + ", " + second + ")"; 
    }

    public A getFirst() {
        return first;
    }

    public void setFirst(A first) {
        this.first = first;
    }

    public B getSecond() {
        return second;
    }

    public void setSecond(B second) {
        this.second = second;
    }
}
arturh
la source
136
Vous voulez probablement supprimer les setters, et faire la première et la seconde finale, rendant ainsi la paire immuable. (Si quelqu'un a changé les composants après les avoir utilisés comme clé de hachage, des choses étranges se produiront).
Thilo
21
retourner "(" + first.toString () + "," + second.toString () + ")" dans la méthode toString () peut lever des NullPointerExceptions. C'est mieux: return "(" + first + "," + second + ")";
Juha Syrjälä
6
Aussi, marquez la paire comme "finale" ou changez la première ligne d'égaux en 'if (other! = Null && this.getClass () == other.getClass ())'
sargas
8
Désolé pour la question aléatoire de nooby, mais pourquoi avez-vous un appel à super () dans le constructeur?
Ibrahim
6
@ Ibrahim: Dans ce cas, c'est superflu --- le comportement est exactement le même si vous super()sortez. Normalement, je le couperais si c'est facultatif, comme ici.
Chris Jester-Young
53

La paire la plus courte que j'ai pu trouver est la suivante, en utilisant Lombok :

@Data
@AllArgsConstructor(staticName = "of")
public class Pair<F, S> {
    private F first;
    private S second;
}

Il a tous les avantages de la réponse de @arturh (sauf la comparabilité), il a hashCode, equals, toStringet un statique « constructeur ».

Michael Piefel
la source
Nifty! Aimé!
Ahmet Ipkin
31

Une autre façon d'implémenter Pair with.

  • Champs publics immuables, c'est-à-dire structure de données simple.
  • Comparable.
  • Hachage simple et égal.
  • Usine simple pour que vous n'ayez pas à fournir les types. par exemple Pair.of ("bonjour", 1);

    public class Pair<FIRST, SECOND> implements Comparable<Pair<FIRST, SECOND>> {
    
        public final FIRST first;
        public final SECOND second;
    
        private Pair(FIRST first, SECOND second) {
            this.first = first;
            this.second = second;
        }
    
        public static <FIRST, SECOND> Pair<FIRST, SECOND> of(FIRST first,
                SECOND second) {
            return new Pair<FIRST, SECOND>(first, second);
        }
    
        @Override
        public int compareTo(Pair<FIRST, SECOND> o) {
            int cmp = compare(first, o.first);
            return cmp == 0 ? compare(second, o.second) : cmp;
        }
    
        // todo move this to a helper class.
        private static int compare(Object o1, Object o2) {
            return o1 == null ? o2 == null ? 0 : -1 : o2 == null ? +1
                    : ((Comparable) o1).compareTo(o2);
        }
    
        @Override
        public int hashCode() {
            return 31 * hashcode(first) + hashcode(second);
        }
    
        // todo move this to a helper class.
        private static int hashcode(Object o) {
            return o == null ? 0 : o.hashCode();
        }
    
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof Pair))
                return false;
            if (this == obj)
                return true;
            return equal(first, ((Pair) obj).first)
                    && equal(second, ((Pair) obj).second);
        }
    
        // todo move this to a helper class.
        private boolean equal(Object o1, Object o2) {
            return o1 == null ? o2 == null : (o1 == o2 || o1.equals(o2));
        }
    
        @Override
        public String toString() {
            return "(" + first + ", " + second + ')';
        }
    }
Peter Lawrey
la source
10
J'aime la méthode d'usine statique of. Cela rappelle les collections immuables de Google Guava .
Jarek Przygódzki
7
Vous êtes à un moment donné coulée o1à Comparable, même si rien ne l' indique appliquera effectivement cette interface. Si c'est une exigence, le FIRSTparamètre type devrait l'être FIRST extends Comparable<?>.
G_H
Je ne suis pas un java, alors pardonnez-moi s'il vous plaît mon ignorance, mais à quel genre de cours d'aide pensiez-vous dans les commentaires TODO?
3
31 est une mauvaise constante pour hashCode. Par exemple, si vous utilisez HashMap avec la clé Pair <Integer, Integer> pour la carte 2D, vous obtiendrez de nombreuses collisions. Par exemple (a * 65497) ^ b serait mieux adapté.
Michał Zieliński
1
@MarioCarneiro ^ est xor, pas le pouvoir
Michał Zieliński
27

Que diriez-vous http://www.javatuples.org/index.html je l'ai trouvé très utile.

Les javatuples vous proposent des classes de tuple de un à dix éléments:

Unit<A> (1 element)
Pair<A,B> (2 elements)
Triplet<A,B,C> (3 elements)
Quartet<A,B,C,D> (4 elements)
Quintet<A,B,C,D,E> (5 elements)
Sextet<A,B,C,D,E,F> (6 elements)
Septet<A,B,C,D,E,F,G> (7 elements)
Octet<A,B,C,D,E,F,G,H> (8 elements)
Ennead<A,B,C,D,E,F,G,H,I> (9 elements)
Decade<A,B,C,D,E,F,G,H,I,J> (10 elements)
cyberoblivion
la source
6
C'est drôle, mais il y a au moins 5 classes de plus que je ne pourrais jamais imaginer utiliser.
maaartinus
3
@maaartinus Au moins 10 de plus que ce que j'utiliserais.
Boann
7
@Boann: OK, je reste corrigé. J'avais l'habitude d'utiliser Pairet je pouvais imaginer utiliser Tripletpeut-être une fois tous les 50 ans. Maintenant, j'utilise Lombok et crée une petite classe de 4 lignes chaque fois que j'ai besoin d'une paire. Donc "10 de trop" est exact.
maaartinus
5
Avons-nous besoin d'un Bottom (0 element)cours? :)
Earth Engine
2
Wow c'est moche. Je sais qu'ils essaient de le rendre explicite, mais un Tuple avec des paramètres surchargés comme en C # aurait été plus agréable.
arviman
12

Cela dépend de l'utilisation que vous souhaitez en faire. La raison typique de le faire est d'itérer sur les cartes, pour lesquelles vous faites simplement cela (Java 5+):

Map<String, Object> map = ... ; // just an example
for (Map.Entry<String, Object> entry : map.entrySet()) {
  System.out.printf("%s -> %s\n", entry.getKey(), entry.getValue());
}
cletus
la source
1
Je ne suis pas sûr qu'un cours personnalisé aiderait dans ce cas :)
Nikita Rybak
31
"La raison typique de le faire est de parcourir les cartes". Vraiment?
Bennett McElwee
12

android fournit la Pairclasse ( http://developer.android.com/reference/android/util/Pair.html ), voici l'implémentation:

public class Pair<F, S> {
    public final F first;
    public final S second;

    public Pair(F first, S second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Pair)) {
            return false;
        }
        Pair<?, ?> p = (Pair<?, ?>) o;
        return Objects.equal(p.first, first) && Objects.equal(p.second, second);
    }

    @Override
    public int hashCode() {
        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
    }

    public static <A, B> Pair <A, B> create(A a, B b) {
        return new Pair<A, B>(a, b);
    }
}
sherpya
la source
1
Objects.equal(..)nécessite la bibliothèque Guava.
Markus L
3
Changez-le Objects.equals(...)en Java depuis 2011 (1.7).
AndrewF
9

Le plus gros problème est probablement que l'on ne peut pas garantir l'immuabilité sur A et B (voir Comment s'assurer que les paramètres de type sont immuables ) donc hashCode()peut donner des résultats incohérents pour la même paire après avoir été insérée dans une collection par exemple (cela donnerait un comportement indéfini , voir Définir des égaux en termes de champs modifiables ). Pour une classe de paires particulière (non générique), le programmeur peut garantir l'immuabilité en choisissant soigneusement A et B comme immuables.

Quoi qu'il en soit, effacer les avertissements génériques de la réponse de @ PeterLawrey (java 1.7):

public class Pair<A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
        implements Comparable<Pair<A, B>> {

    public final A first;
    public final B second;

    private Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }

    public static <A extends Comparable<? super A>,
                    B extends Comparable<? super B>>
            Pair<A, B> of(A first, B second) {
        return new Pair<A, B>(first, second);
    }

    @Override
    public int compareTo(Pair<A, B> o) {
        int cmp = o == null ? 1 : (this.first).compareTo(o.first);
        return cmp == 0 ? (this.second).compareTo(o.second) : cmp;
    }

    @Override
    public int hashCode() {
        return 31 * hashcode(first) + hashcode(second);
    }

    // TODO : move this to a helper class.
    private static int hashcode(Object o) {
        return o == null ? 0 : o.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Pair))
            return false;
        if (this == obj)
            return true;
        return equal(first, ((Pair<?, ?>) obj).first)
                && equal(second, ((Pair<?, ?>) obj).second);
    }

    // TODO : move this to a helper class.
    private boolean equal(Object o1, Object o2) {
        return o1 == o2 || (o1 != null && o1.equals(o2));
    }

    @Override
    public String toString() {
        return "(" + first + ", " + second + ')';
    }
}

Ajouts / corrections très appréciés :) En particulier, je ne suis pas sûr de mon utilisation Pair<?, ?>.

Pour plus d'informations sur les raisons de cette syntaxe, voir Assurez - vous que les objets implémentent Comparable et pour une explication détaillée Comment implémenter une max(Comparable a, Comparable b)fonction générique en Java?

Mr_and_Mrs_D
la source
Comme les nombres entiers Java sont 32 bits, la multiplication du premier code de hachage par 31 ne signifie-t-elle pas qu'il déborde? Ne vaudrait-il pas mieux effectuer un OU exclusif?
Dan
@Dan n'hésitez pas à modifier Je me suis éloigné de java :)
Mr_and_Mrs_D
5

À mon avis, il n'y a pas de paire en Java car, si vous souhaitez ajouter des fonctionnalités supplémentaires directement sur la paire (par exemple, comparable), vous devez délimiter les types. En C ++, nous ne nous en soucions pas, et si les types composant une paire n'en ont pas operator <, pair::operator <ils ne se compileront pas aussi bien.

Un exemple de comparable sans limite:

public class Pair<F, S> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static int compare(Object l, Object r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : ((Comparable) (l)).compareTo(r);
        }
    }
}

/* ... */

Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
//Runtime error here instead of compile error!
System.out.println(a.compareTo(b));

Un exemple de Comparable avec la vérification à la compilation pour savoir si les arguments de type sont comparables:

public class Pair<
        F extends Comparable<? super F>, 
        S extends Comparable<? super S>
> implements Comparable<Pair<? extends F, ? extends S>> {
    public final F first;
    public final S second;
    /* ... */
    public int compareTo(Pair<? extends F, ? extends S> that) {
        int cf = compare(first, that.first);
        return cf == 0 ? compare(second, that.second) : cf;
    }
    //Why null is decided to be less than everything?
    private static <
            T extends Comparable<? super T>
    > int compare(T l, T r) {
        if (l == null) {
            return r == null ? 0 : -1;
        } else {
            return r == null ? 1 : l.compareTo(r);
        }
    }
}

/* ... */

//Will not compile because Thread is not Comparable<? super Thread>
Pair<Thread, HashMap<String, Integer>> a = /* ... */;
Pair<Thread, HashMap<String, Integer>> b = /* ... */;
System.out.println(a.compareTo(b));

C'est bien, mais cette fois, vous ne pouvez pas utiliser des types non comparables comme arguments de type dans Pair. On peut utiliser beaucoup de comparateurs pour Pair dans certaines classes utilitaires, mais les gens C ++ peuvent ne pas l'obtenir. Une autre façon est d'écrire beaucoup de classes dans une hiérarchie de types avec différentes limites sur les arguments de type, mais il y a trop de limites possibles et leurs combinaisons ...

MaxBuzz
la source
5

JavaFX (fourni avec Java 8) a la classe Pair <A, B>

CarrKnight
la source
1
L' implémentation de hashCode dans javafx.util.Pair peut conduire à des collisions sur des cas triviaux. Son utilisation dans HashMap / HashTable fonctionnera toujours puisque Java vérifie l'égalité des valeurs en plus des codes de hachage, mais c'est quelque chose à savoir.
sffc
Il s'agit d'une implémentation de hashCode très standard et couramment recommandée. Les collisions doivent être attendues par tout code qui appelle hashCode(). Notez que Java lui-même n'appelle pas cette méthode. C'est pour le code utilisateur, y compris les bibliothèques.
AndrewF
5

Comme beaucoup d'autres l'ont déjà dit, cela dépend vraiment du cas d'utilisation si une classe Pair est utile ou non.

Je pense que pour une fonction d'assistance privée, il est tout à fait légitime d'utiliser une classe Pair si cela rend votre code plus lisible et ne vaut pas la peine de créer une autre classe de valeur avec tout son code de plaque de chaudière.

D'un autre côté, si votre niveau d'abstraction vous oblige à documenter clairement la sémantique de la classe qui contient deux objets ou valeurs, alors vous devez écrire une classe pour elle. C'est généralement le cas si les données sont un objet métier.

Comme toujours, cela nécessite un jugement avisé.

Pour votre deuxième question, je recommande la classe Pair des bibliothèques Apache Commons. Celles-ci peuvent être considérées comme des bibliothèques standard étendues pour Java:

https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/Pair.html

Vous pouvez également consulter EqualsBuilder , HashCodeBuilder et ToStringBuilder d' Apache Commons qui simplifient l'écriture des classes de valeur pour vos objets métier.

Peter Goetz
la source
L'URL mise à jour est commons.apache.org/lang/api-release/index.html?org/apache/… car commons-lang3 n'est plus en version bêta. C'est encore plus court que ma propre solution Lombok si vous utilisez déjà commons-lang 3.
Michael Piefel
5

Les bonnes nouvelles JavaFXont une paire de valeurs clés.

ajoutez simplement javafx en tant que dépendance et importez javafx.util.Pair;

et utilisez simplement comme dans c++.

Pair <Key, Value> 

par exemple

Pair <Integer, Integer> pr = new Pair<Integer, Integer>()

pr.get(key);// will return corresponding value
RAJAN_PARMAR
la source
La mauvaise nouvelle est que tout le monde n'utilise pas JavaFX
Michał Dobi Dobrzański
4

L' interface Map.Entry est assez proche de la paire c ++. Regardez l'implémentation concrète, comme AbstractMap.SimpleEntry et AbstractMap.SimpleImmutableEntry Le premier élément est getKey () et le second est getValue ().

Nikunj Bhagat
la source
1
OP est déjà au courant de cette option, et elle a été longuement discutée.
Keegan
4
Collections.singletonMap(left, rigth);
Denis Arkharov
la source
3

Selon la nature du langage Java, je suppose que les gens n'ont pas réellement besoin d'un Pair, une interface est généralement ce dont ils ont besoin. Voici un exemple:

interface Pair<L, R> {
    public L getL();
    public R getR();
}

Ainsi, lorsque les utilisateurs souhaitent renvoyer deux valeurs, ils peuvent effectuer les opérations suivantes:

... //Calcuate the return value
final Integer v1 = result1;
final String v2 = result2;
return new Pair<Integer, String>(){
    Integer getL(){ return v1; }
    String getR(){ return v2; }
}

C'est une solution assez légère, et elle répond à la question "Quelle est la sémantique d'un Pair<L,R>?". La réponse est, il s'agit d'une interface avec deux types (peut-être différents), et il a des méthodes pour retourner chacun d'eux. C'est à vous d'y ajouter plus de sémantique. Par exemple, si vous utilisez Position et voulez VRAIMENT l'indiquer dans votre code, vous pouvez définir PositionXet PositionYqui contient Integer, pour constituer un Pair<PositionX,PositionY>. Si JSR 308 est disponible, vous pouvez également utiliser Pair<@PositionX Integer, @PositionY Ingeger>pour simplifier cela.

EDIT: Une chose que je dois indiquer ici est que la définition ci-dessus relie explicitement le nom du paramètre de type et le nom de la méthode. C'est une réponse à ceux qui soutiennent qu'il y a un Pairmanque d'informations sémantiques. En fait, la méthode getLsignifie "donnez-moi l'élément qui correspond au type de paramètre de type L", ce qui signifie quelque chose.

EDIT: Voici une classe utilitaire simple qui peut vous faciliter la vie:

class Pairs {
    static <L,R> Pair<L,R> makePair(final L l, final R r){
        return new Pair<L,R>(){
            public L getL() { return l; }
            public R getR() { return r; }   
        };
    }
}

usage:

return Pairs.makePair(new Integer(100), "123");
Earth Engine
la source
Qu'en est- il equals, hashCodeet toString?
sdgfsdh
eh bien, ce n'est qu'une implémentation minimale. Si vous avez besoin de plus que cela, vous pouvez écrire des fonctions d'assistance pour le rendre plus facile, mais vous devez toujours écrire le code.
Earth Engine
Pour l'implémenter, toStringvous avez besoin de plus de connaissances sur la relation entre les deux champs.
Earth Engine
Mon point est de fournir un classpourrait être mieux qu'un simple interfacecar il peut mettre en œuvre ces choses.
sdgfsdh
3

Bien qu'ils soient syntaxiquement similaires, Java et C ++ ont des paradigmes très différents. Écrire C ++ comme Java est mauvais C ++, et écrire Java comme C ++ est mauvais Java.

Avec un IDE basé sur la réflexion comme Eclipse, l'écriture des fonctionnalités nécessairement d'une classe "pair" est rapide et simple. Créez une classe, définissez deux champs, utilisez les différentes options du menu "Générer XX" pour remplir la classe en quelques secondes. Vous devrez peut-être taper un "compareTo" très rapidement si vous vouliez l'interface Comparable.

Avec des options de déclaration / définition distinctes dans le langage, les générateurs de code C ++ ne sont pas si bons, donc l'écriture manuelle de petites classes utilitaires prend plus de temps. Parce que la paire est un modèle, vous n'avez pas à payer pour les fonctions que vous n'utilisez pas, et la fonction typedef permet d'attribuer des noms de caractères significatifs au code, de sorte que les objections concernant "pas de sémantique" ne tiennent pas vraiment.

gerardw
la source
2

La paire serait une bonne chose, pour être une unité de construction de base pour des génériques complexes, par exemple, cela vient de mon code:

WeakHashMap<Pair<String, String>, String> map = ...

C'est la même chose que le tuple de Haskell

Illarion Kovalchuk
la source
1
Maintenant, je peux dire que l'utilisation de Pair <A, B> rend le code moins informatif, et l'implémentation d'objets spéciaux au lieu d'utiliser Pair est beaucoup mieux
Illarion Kovalchuk
1
Mieux ou pire. Imaginez que vous ayez une fonction combinant ses deux arguments (par exemple la fusion de graphiques en un seul) et que vous ayez besoin de la mettre en cache. Ici, Pairc'est optimal car il n'y a pas de sémantique particulière. Avoir un nom clair pour un concept clair est bien, mais chercher un nom où "premier" et "second" fonctionnent bien ne l'est pas.
maaartinus
2

Objet de manière simple [] - peut être utilisé comme n'importe quel tuple de dimension

Testus
la source
2
N'importe quelle dimension, oui. Mais: lourd à créer et non sûr pour le type.
Michael Piefel
2

Pour les langages de programmation comme Java, la structure de données alternative utilisée par la plupart des programmeurs pour représenter des structures de données de type paire est deux tableaux, et les données sont accessibles via le même index

exemple: http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080

Ce n'est pas idéal car les données doivent être liées ensemble, mais elles s'avèrent également assez bon marché. De plus, si votre cas d'utilisation nécessite de stocker des coordonnées, il est préférable de créer votre propre structure de données.

J'ai quelque chose comme ça dans ma bibliothèque

public class Pair<First,Second>{.. }
Swapneel Patil
la source
2

Vous pouvez utiliser la bibliothèque AutoValue de Google - https://github.com/google/auto/tree/master/value .

Vous créez une très petite classe abstraite et l'annotez avec @AutoValue et le processeur d'annotation génère pour vous une classe concrète qui a une valeur sémantique.

Shahar
la source
2

Voici quelques bibliothèques qui ont plusieurs degrés de tuples pour votre commodité:

  • JavaTuples . Tuples de degré 1-10 est tout ce qu'il a.
  • JavaSlang . Tuples de degré 0-8 et beaucoup d'autres goodies fonctionnels.
  • jOOλ . Tuples de degré 0-16 et quelques autres goodies fonctionnels. (Avertissement, je travaille pour la société de maintenance)
  • Java fonctionnel . Tuples de degré 0-8 et beaucoup d'autres goodies fonctionnels.

D'autres bibliothèques ont été mentionnées comme contenant au moins le Pairtuple.

Plus précisément, dans le contexte d'une programmation fonctionnelle qui utilise beaucoup de typage structurel, plutôt que de typage nominal ( comme le préconise la réponse acceptée ), ces bibliothèques et leurs tuples sont très utiles.

Lukas Eder
la source
2

une autre implémentation laconique de lombok

import lombok.Value;

@Value(staticConstructor = "of")
public class Pair<F, S> {
    private final F first;
    private final S second;
}
Matt Broekhuis
la source
1

J'ai remarqué que toutes les implémentations de paires éparpillées ici attribuent une signification à l'ordre des deux valeurs. Quand je pense à une paire, je pense à une combinaison de deux éléments dans lesquels l'ordre des deux n'a pas d'importance. Voici mon implémentation d'une paire non ordonnée, avec hashCodeet equalsremplaçant pour garantir le comportement souhaité dans les collections. Également clonable.

/**
 * The class <code>Pair</code> models a container for two objects wherein the
 * object order is of no consequence for equality and hashing. An example of
 * using Pair would be as the return type for a method that needs to return two
 * related objects. Another good use is as entries in a Set or keys in a Map
 * when only the unordered combination of two objects is of interest.<p>
 * The term "object" as being a one of a Pair can be loosely interpreted. A
 * Pair may have one or two <code>null</code> entries as values. Both values
 * may also be the same object.<p>
 * Mind that the order of the type parameters T and U is of no importance. A
 * Pair&lt;T, U> can still return <code>true</code> for method <code>equals</code>
 * called with a Pair&lt;U, T> argument.<p>
 * Instances of this class are immutable, but the provided values might not be.
 * This means the consistency of equality checks and the hash code is only as
 * strong as that of the value types.<p>
 */
public class Pair<T, U> implements Cloneable {

    /**
     * One of the two values, for the declared type T.
     */
    private final T object1;
    /**
     * One of the two values, for the declared type U.
     */
    private final U object2;
    private final boolean object1Null;
    private final boolean object2Null;
    private final boolean dualNull;

    /**
     * Constructs a new <code>Pair&lt;T, U&gt;</code> with T object1 and U object2 as
     * its values. The order of the arguments is of no consequence. One or both of
     * the values may be <code>null</code> and both values may be the same object.
     *
     * @param object1 T to serve as one value.
     * @param object2 U to serve as the other value.
     */
    public Pair(T object1, U object2) {

        this.object1 = object1;
        this.object2 = object2;
        object1Null = object1 == null;
        object2Null = object2 == null;
        dualNull = object1Null && object2Null;

    }

    /**
     * Gets the value of this Pair provided as the first argument in the constructor.
     *
     * @return a value of this Pair.
     */
    public T getObject1() {

        return object1;

    }

    /**
     * Gets the value of this Pair provided as the second argument in the constructor.
     *
     * @return a value of this Pair.
     */
    public U getObject2() {

        return object2;

    }

    /**
     * Returns a shallow copy of this Pair. The returned Pair is a new instance
     * created with the same values as this Pair. The values themselves are not
     * cloned.
     *
     * @return a clone of this Pair.
     */
    @Override
    public Pair<T, U> clone() {

        return new Pair<T, U>(object1, object2);

    }

    /**
     * Indicates whether some other object is "equal" to this one.
     * This Pair is considered equal to the object if and only if
     * <ul>
     * <li>the Object argument is not null,
     * <li>the Object argument has a runtime type Pair or a subclass,
     * </ul>
     * AND
     * <ul>
     * <li>the Object argument refers to this pair
     * <li>OR this pair's values are both null and the other pair's values are both null
     * <li>OR this pair has one null value and the other pair has one null value and
     * the remaining non-null values of both pairs are equal
     * <li>OR both pairs have no null values and have value tuples &lt;v1, v2> of
     * this pair and &lt;o1, o2> of the other pair so that at least one of the
     * following statements is true:
     * <ul>
     * <li>v1 equals o1 and v2 equals o2
     * <li>v1 equals o2 and v2 equals o1
     * </ul>
     * </ul>
     * In any other case (such as when this pair has two null parts but the other
     * only one) this method returns false.<p>
     * The type parameters that were used for the other pair are of no importance.
     * A Pair&lt;T, U> can return <code>true</code> for equality testing with
     * a Pair&lt;T, V> even if V is neither a super- nor subtype of U, should
     * the the value equality checks be positive or the U and V type values
     * are both <code>null</code>. Type erasure for parameter types at compile
     * time means that type checks are delegated to calls of the <code>equals</code>
     * methods on the values themselves.
     *
     * @param obj the reference object with which to compare.
     * @return true if the object is a Pair equal to this one.
     */
    @Override
    public boolean equals(Object obj) {

        if(obj == null)
            return false;

        if(this == obj)
            return true;

        if(!(obj instanceof Pair<?, ?>))
            return false;

        final Pair<?, ?> otherPair = (Pair<?, ?>)obj;

        if(dualNull)
            return otherPair.dualNull;

        //After this we're sure at least one part in this is not null

        if(otherPair.dualNull)
            return false;

        //After this we're sure at least one part in obj is not null

        if(object1Null) {
            if(otherPair.object1Null) //Yes: this and other both have non-null part2
                return object2.equals(otherPair.object2);
            else if(otherPair.object2Null) //Yes: this has non-null part2, other has non-null part1
                return object2.equals(otherPair.object1);
            else //Remaining case: other has no non-null parts
                return false;
        } else if(object2Null) {
            if(otherPair.object2Null) //Yes: this and other both have non-null part1
                return object1.equals(otherPair.object1);
            else if(otherPair.object1Null) //Yes: this has non-null part1, other has non-null part2
                return object1.equals(otherPair.object2);
            else //Remaining case: other has no non-null parts
                return false;
        } else {
            //Transitive and symmetric requirements of equals will make sure
            //checking the following cases are sufficient
            if(object1.equals(otherPair.object1))
                return object2.equals(otherPair.object2);
            else if(object1.equals(otherPair.object2))
                return object2.equals(otherPair.object1);
            else
                return false;
        }

    }

    /**
     * Returns a hash code value for the pair. This is calculated as the sum
     * of the hash codes for the two values, wherein a value that is <code>null</code>
     * contributes 0 to the sum. This implementation adheres to the contract for
     * <code>hashCode()</code> as specified for <code>Object()</code>. The returned
     * value hash code consistently remain the same for multiple invocations
     * during an execution of a Java application, unless at least one of the pair
     * values has its hash code changed. That would imply information used for 
     * equals in the changed value(s) has also changed, which would carry that
     * change onto this class' <code>equals</code> implementation.
     *
     * @return a hash code for this Pair.
     */
    @Override
    public int hashCode() {

        int hashCode = object1Null ? 0 : object1.hashCode();
        hashCode += (object2Null ? 0 : object2.hashCode());
        return hashCode;

    }

}

Cette implémentation a été correctement testée à l'unité et son utilisation dans un ensemble et une carte a été testée.

Remarquez que je ne prétends pas publier cela dans le domaine public. Ceci est du code que je viens d'écrire pour une utilisation dans une application, donc si vous allez l'utiliser, veuillez vous abstenir de faire une copie directe et déranger les commentaires et les noms un peu. Attraper ma dérive?

G_H
la source
3
en fait, consultez le bas de chaque page: "contributions des utilisateurs sous licence cc-wiki"
amara
Ah, je ne l'avais pas remarqué. Merci pour l'information. Dans ce cas, utilisez le code comme bon vous semble sous cette licence.
G_H
1
La question concerne une paire équivalente C ++ - qui est commandée. Je pense aussi que tant que l'on a une référence à l'objet Pair et que ceux-ci sont mutables, l'insertion de paires dans des collections peut conduire à un comportement indéfini.
Mr_and_Mrs_D
1

com.sun.tools.javac.util.Pair est une implémentation simple d'une paire. Il se trouve dans jdk1.7.0_51 \ lib \ tools.jar.

À part org.apache.commons.lang3.tuple.Pair, ce n'est pas seulement une interface.

masterxilo
la source
2
Cependant, personne ne devrait utiliser les API internes JDK.
jpangamarca
0
public class Pair<K, V> {

    private final K element0;
    private final V element1;

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

    public Pair(K element0, V element1) {
        this.element0 = element0;
        this.element1 = element1;
    }

    public K getElement0() {
        return element0;
    }

    public V getElement1() {
        return element1;
    }

}

utilisation:

Pair<Integer, String> pair = Pair.createPair(1, "test");
pair.getElement0();
pair.getElement1();

Immuable, seulement une paire!

Bastiflew
la source
Oh wow. Un autre? Essayez d'utiliser le vôtre avec des génériques plus complexes - à un moment donné, il ne parviendra pas à déduire les types appropriés. De plus, ce qui suit devrait être possible: Pair<Object, Object> pair = Pair.createPair("abc", "def")mais je pense qu'il faut écrire Pair.createPair((Object)"abc", (Object)"def")avec votre code?
A QUIT - Anony-Mousse
vous pouvez remplacer la méthode statique par ceci: @SuppressWarnings("unchecked") public static <K, V, X, Y> Pair<X, Y> createPair(K key, V value) { return new Pair<X, Y>((X) key, (Y) value); } mais je ne sais pas si c'est une bonne pratique
Bastiflew
Non, cela ne fera que gâcher encore plus les choses. D'après mon expérience, au moins l'un des compilateurs (essayez le compilateur java6, java7, javadoc et eclipse java) se plaindra. Le traditionnel new Pair<Object, Object>("abc", "def")était le plus fiable de mes expériences.
A QUIT - Anony-Mousse