Qual è l'equivalente della coppia C ++ <L, R> in Java?


671

C'è una buona ragione per cui non ce n'è Pair<L,R>in Java? Quale sarebbe l'equivalente di questo costrutto C ++? Preferirei evitare di reimplementare il mio.

Sembra che 1.6 stia fornendo qualcosa di simile ( AbstractMap.SimpleEntry<K,V>), ma questo sembra abbastanza contorto.


7
Perché è AbstractMap.SimpleEntrycontorto?
CurtainDog

27
A causa del nome, arbitraria denominazione di una chiave e un valore.
Enerccio,


2
@sffc JavaFX non si trova su nessuno dei percorsi di classe predefiniti in JDK7, il suo utilizzo richiede l'aggiunta manuale delle librerie di runtime JFX.
Cord Rehn,

3
@Enerccio: Quindi, affermi che "primo" e "secondo" non sono arbitrari, mentre "chiave" e "valore" - lo sono? Quindi questa è una buona ragione per non avere tale classe in SDK. Ci sarebbe una disputa eterna sulla denominazione "corretta".
fdreger,

Risposte:


400

In un thread sucomp.lang.java.help , Hunter Gratzner fornisce alcuni argomenti contro la presenza di un Paircostrutto in Java. L'argomento principale è che una classe Pairnon trasmette alcuna semantica sulla relazione tra i due valori (come fai a sapere cosa significano "primo" e "secondo"?).

Una pratica migliore è scrivere una classe molto semplice, come quella proposta da Mike, per ogni applicazione che avresti fatto della Pairclasse. Map.Entryè un esempio di coppia che porta il suo significato nel suo nome.

Per riassumere, secondo me è meglio avere una classe Position(x,y), una classe Range(begin,end)e una classe Entry(key,value)piuttosto che un generico Pair(first,second)che non mi dica nulla di ciò che dovrebbe fare.


143
Gratzner sta spaccando i peli. Siamo abbastanza felici di restituire un singolo valore come classe primitiva o incorporata senza incapsularlo in una classe. Se dovessimo restituire una tupla di una dozzina di elementi nessuno sarebbe in disaccordo, dovrebbe avere una propria classe. Da qualche parte nel mezzo c'è una linea di demarcazione (sfocata). Penso che i nostri cervelli di lucertola possano far fronte alle coppie abbastanza facilmente.
Ian,

25
Sono d'accordo con Ian. Java ti consente di restituire int; non ti obbliga a creare un alias per int ogni volta che ne usi uno. Le coppie non sono molto diverse.
Clément,

5
Se potessimo decomprimere una coppia direttamente nelle variabili locali o inoltrarla a un metodo che accetta due argomenti, Pair sarebbe una classe utile. Dal momento che non possiamo decomprimerlo in questo modo, creare una classe significativa e mantenere i valori insieme non sembra male. E, se vuoi davvero una coppia nonostante le limitazioni, c'è sempre Object [2] + cast :-)
marcus

Il fatto è che se non sei d'accordo con Gratzner, allora ci sono implementazioni di Pair in diversi posti. Apache Commons e Guava lo hanno entrambi IIRC. Usa quelli. Ma mettere qualcosa nelle principali librerie Java significa che è un modo nobile e approvato di fare le cose (con le maiuscole) e poiché le persone non sono d'accordo su questo, non dovremmo metterlo lì. C'è abbastanza innesto nelle vecchie librerie così com'è, non inutilmente mettiamo più lì.
Haakon Løtveit,

1
@Dragas Quando ho bisogno di una coppia di valori, allora non è Java ... sul serio?
idclev 463035818,

156

Questo è Java. Devi creare la tua classe Pair personalizzata con nomi di campi e classi descrittivi e non ti preoccupare che reinventerai la ruota scrivendo hashCode () / equals () o implementando Comparable ancora e ancora.


61
Questo non risponde alla domanda "perché". (A meno che tu non consideri 'this is java' una risposta)
Nikita Rybak il

127
+1 per deridere la verbosità di Java. -1 per non aver effettivamente risposto alla domanda.
Bennett McElwee,

19
La beffa di Java sarebbe andata bene se avessi indicato Apache Commong Lang, che contiene una classe Pair.
haylem,

6
Oppure potresti semplicemente usareSimpleImmutableEntry
CurtainDog,

33
La prima frase risponde alla domanda "perché?". Questo è Java e basta.
Masterziv,

103

Classe di coppia compatibile con 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;
    }
}

136
Probabilmente vuoi eliminare i setter e rendere la prima e la seconda finale, rendendo così immutabile la coppia. (Se qualcuno ha cambiato i componenti dopo averli usati come chiave hash, accadranno cose strane).
Thilo,

21
return "(" + first.toString () + "," + second.toString () + ")" nel metodo toString () può generare NullPointerExceptions. Questo è meglio: return "(" + first + "," + second + ")";
Juha Syrjälä,

6
Inoltre, o contrassegna la coppia come "finale" o modifica la prima riga di uguale a 'if (altro! = Null && this.getClass () == other.getClass ())'
sargas,

8
Ci scusiamo per la domanda casuale su Nooby, ma perché hai una chiamata a super () nel costruttore?
Ibrahim,

6
@Ibrahim: In questo caso, è superfluo --- il comportamento è esattamente lo stesso se hai super()eliminato. Normalmente lo taglierei semplicemente se è facoltativo, come se fosse qui.
Chris Jester-Young,

53

La coppia più corta che ho potuto inventare è la seguente, usando Lombok :

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

Ha tutti i vantaggi di una risposta da @arturh (tranne la comparabilità), ha hashCode, equals, toStringe un “costruttore” statica.


Nifty! Mi è piaciuto!
Ahmet Ipkin,


31

Un altro modo per implementare Pair with.

  • Campi pubblici immutabili, ovvero semplici strutture di dati.
  • Paragonabile.
  • Hash semplice e uguale.
  • Fabbrica semplice, quindi non è necessario fornire i tipi. es. Pair.of ("hello", 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 + ')';
        }
    }
    

10
Mi piace il metodo statico di fabbrica of. Ricorda le collezioni immutabili di Google Guava .
Jarek Przygódzki,

7
Siete ad un certo punto la fusione o1a Comparable, anche se nulla indica che sarà effettivamente attuare tale interfaccia. Se questo è un requisito, FIRSTdovrebbe essere il parametro type FIRST extends Comparable<?>.
G_H,

Non sono un tipo java, quindi ti prego di perdonarmi per la mia ignoranza, ma a che tipo di classi di aiuto stavi pensando nei commenti di TODO?

3
31 è una costante errata per hashCode. Ad esempio, se si utilizza HashMap codificato da Pair <Integer, Integer> per la mappa 2D, si otterranno molte collisioni. Ad esempio (a * 65497) ^ b sarebbe più adatto.
Michał Zieliński,

1
@MarioCarneiro ^ is xor, not power
Michał Zieliński

27

Che ne dici di http://www.javatuples.org/index.html l' ho trovato molto utile.

Il javatuples ti offre classi di tuple da uno a dieci elementi:

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)

6
Divertente, ma ci sono almeno 5 classi in più di quanto potrei mai immaginare di usare.
maaartinus,

3
@maaartinus Almeno 10 in più di quanto userei.
Boann,

7
@Boann: OK, rimango corretto. Usavo Paire immaginavo di usarlo Tripletforse una volta ogni 50 anni. Ora uso Lombok e creo una minuscola classe a 4 righe ogni volta che ho bisogno di una coppia. Quindi "10 troppo" è esatto.
maaartinus,

5
Abbiamo bisogno di una Bottom (0 element)lezione? :)
Earth Engine

2
Wow, questo è brutto. So che stanno cercando di renderlo esplicito, ma una Tupla con parametri sovraccarichi come in C # sarebbe stata più carina.
Arviman,

12

Dipende da cosa vuoi usarlo. Il motivo tipico per farlo è iterare sulle mappe, per le quali è sufficiente farlo (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());
}

1
Non sono sicuro che una lezione personalizzata possa aiutare in questo caso :)
Nikita Rybak,

31
"Il motivo tipico per farlo è di scorrere le mappe". Veramente?
Bennett McElwee,

12

Android fornisce Pairclasse ( http://developer.android.com/reference/android/util/Pair.html ), qui l'implementazione:

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

1
Objects.equal(..)richiede la libreria Guava.
Markus L

3
Modificalo su Objects.equals(...)quale è stato in Java dal 2011 (1.7).
AndrewF,

9

Il problema più grande è probabilmente che non si può garantire l'immutabilità su A e B (vedere Come assicurarsi che i parametri del tipo siano immutabili ), quindi si hashCode()potrebbero dare risultati incoerenti per la stessa coppia dopo che è stato inserito in una raccolta, ad esempio (questo darebbe un comportamento indefinito , vedere Definizione di uguali in termini di campi modificabili ). Per una particolare classe di coppie (non generiche) il programmatore può garantire l'immutabilità scegliendo accuratamente A e B come immutabili.

Ad ogni modo, cancellando gli avvisi di Generic dalla risposta di @ 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 + ')';
    }
}

Aggiunte / correzioni molto gradite :) In particolare non sono del tutto sicuro del mio uso di Pair<?, ?>.

Per ulteriori informazioni sul perché questa sintassi, consultare Accertarsi che gli oggetti implementino Comparable e per una spiegazione dettagliata Come implementare una max(Comparable a, Comparable b)funzione generica in Java?


Dato che i numeri interi Java sono a 32 bit, moltiplicare il primo hashcode per 31 non significa che trabocca? Non sarebbe meglio eseguire un OR esclusivo?
Dan

@Dan sentiti libero di modificare Mi sono allontanato da Java :)
Mr_and_Mrs_D il

5

A mio avviso, non esiste una coppia in Java perché, se si desidera aggiungere funzionalità extra direttamente alla coppia (ad es. Confrontabile), è necessario associare i tipi. In C ++, non ci interessa, e se i tipi che compongono una coppia non ne hanno operator <, pair::operator <non verranno compilati.

Un esempio di Comparable senza limiti:

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 esempio di Comparable with compile-time check per verificare se gli argomenti di tipo sono comparabili:

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

Questo va bene, ma questa volta non puoi usare tipi non confrontabili come argomenti di tipo in Pair. Uno può usare molti Comparatori per Pair in alcune classi di utilità, ma le persone C ++ potrebbero non ottenerlo. Un altro modo è scrivere molte classi in una gerarchia di tipi con limiti diversi su argomenti di tipo, ma ci sono troppi limiti possibili e le loro combinazioni ...


5

JavaFX (che viene fornito in bundle con Java 8) ha la classe Pair <A, B>


1
L' implementazione di hashCode in javafx.util.Pair può portare a collisioni su casi banali. Usarlo in HashMap / HashTable continuerà a funzionare poiché Java verifica l'uguaglianza dei valori oltre ai codici hash, ma è qualcosa di cui tenere conto.
Sffc,

Questa è un'implementazione hashCode molto standard e comunemente raccomandata. Le collisioni dovrebbero essere previste da qualsiasi codice che chiama hashCode(). Nota che Java stesso non chiama questo metodo. È per il codice utente, comprese le librerie.
AndrewF,

5

Come molti altri hanno già affermato, dipende davvero dal caso d'uso se una classe Pair sia utile o meno.

Penso che per una funzione di aiuto privato sia totalmente legittimo usare una classe Pair se questo rende il tuo codice più leggibile e non vale la pena di creare un'altra classe di valore con tutto il suo codice di targa della caldaia.

D'altra parte, se il tuo livello di astrazione richiede di documentare chiaramente la semantica della classe che contiene due oggetti o valori, allora dovresti scrivere una classe per esso. Di solito è così se i dati sono un oggetto business.

Come sempre, richiede un giudizio competente.

Per la tua seconda domanda, raccomando la classe Pair dalle librerie di Apache Commons. Quelle potrebbero essere considerate librerie standard estese per Java:

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

Potresti anche dare un'occhiata a EqualsBuilder , HashCodeBuilder e ToStringBuilder di Apache Commons che semplificano la scrittura di classi di valore per i tuoi oggetti business.


L'URL aggiornato è commons.apache.org/lang/api-release/index.html?org/apache/… poiché commons-lang3 non è in versione beta. Questo è ancora più breve della mia soluzione Lombok se usi già commons-lang 3.
Michael Piefel


5

Le buone notizie JavaFXhanno un valore-chiave Pair.

aggiungi javafx come dipendenza e importa javafx.util.Pair;

e usare semplicemente come in c++.

Pair <Key, Value> 

per esempio

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

pr.get(key);// will return corresponding value

La cattiva notizia è che non tutti usano JavaFX
Michał Dobi Dobrzański

4

L' interfaccia Map.Entry si avvicina molto alla coppia c ++. Guarda l'implementazione concreta, come AbstractMap.SimpleEntry e AbstractMap.SimpleImmutableEntry Il primo elemento è getKey () e il secondo è getValue ().


1
OP è già a conoscenza di questa opzione ed è stata discussa a lungo.
Keegan,


3

Secondo la natura del linguaggio Java, suppongo che le persone non abbiano effettivamente bisogno di un'interfaccia Pair, di solito è ciò di cui hanno bisogno. Ecco un esempio:

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

Quindi, quando le persone vogliono restituire due valori, possono fare quanto segue:

... //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; }
}

Questa è una soluzione piuttosto leggera e risponde alla domanda "Qual è la semantica di un Pair<L,R>?". La risposta è che si tratta di un'interfaccia costruita con due tipi (che possono essere diversi) e ha metodi per restituire ciascuno di essi. Sta a te aggiungere ulteriore semantico ad esso. Ad esempio, se stai usando Posizione e VERAMENTE vuoi indicarlo nel tuo codice, puoi definire PositionXe PositionYche contiene Integer, per comporre un Pair<PositionX,PositionY>. Se è disponibile JSR 308, è possibile utilizzare anche Pair<@PositionX Integer, @PositionY Ingeger>per semplificarlo.

EDIT: Una cosa che dovrei indicare qui è che la definizione di cui sopra si riferisce esplicitamente al nome del parametro di tipo e al nome del metodo. Questa è una risposta a coloro che sostengono che vi sia una Pairmancanza di informazioni semantiche. In realtà, il metodo getLsignifica "dammi l'elemento che corrisponde al tipo di parametro tipo L", che significa qualcosa.

EDIT: Ecco una semplice classe di utilità che può semplificare la vita:

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

utilizzo:

return Pairs.makePair(new Integer(100), "123");

Che dire equals, hashCodee toString?
sdgfsdh,

bene, questa è solo un'implementazione minima. Se hai bisogno di qualcosa di più, puoi scrivere alcune funzioni di supporto per renderlo più semplice, ma devi comunque scrivere il codice.
Earth Engine

Per implementare toStringè necessaria una maggiore conoscenza della relazione tra i due campi.
Earth Engine

Il mio punto è fornire un classpotrebbe essere migliore di un semplice interfaceperché può implementare queste cose.
sdgfsdh,

3

Nonostante siano sintatticamente simili, Java e C ++ hanno paradigmi molto diversi. Scrivere C ++ come Java è cattivo C ++ e scrivere Java come C ++ è cattivo Java.

Con un IDE basato sulla riflessione come Eclipse, scrivere la funzionalità necessariamente di una classe "coppia" è semplice e veloce. Crea classe, definisci due campi, usa le varie opzioni del menu "Genera XX" per compilare la classe in pochi secondi. Forse dovresti digitare un "compareTo" molto velocemente se desideri l'interfaccia comparabile.

Con opzioni di dichiarazione / definizione separate nella lingua, i generatori di codice C ++ non sono così buoni, quindi scrivere a mano piccole classi di utilità richiede più tempo. Poiché la coppia è un modello, non è necessario pagare per le funzioni che non si utilizzano e la funzione typedef consente di assegnare nomi di tipo significativi al codice, quindi le obiezioni su "nessuna semantica" non reggono davvero.


2

La coppia sarebbe una buona cosa, essere un'unità di costruzione di base per generici complessi, ad esempio, questo è dal mio codice:

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

È lo stesso della Tupla di Haskell


1
Ora posso dire che usare Pair <A, B> rende il codice meno informativo e implementare oggetti speciali invece di usare Pair è molto meglio
Illarion Kovalchuk,

1
Meglio o peggio. Immagina di avere una funzione che combina i suoi due argomenti (ad esempio unendo i grafici in uno) e che devi memorizzarli nella cache. Qui, Pairè ottimale in quanto non esiste una semantica speciale. Avere un nome chiaro per un concetto chiaro è buono, ma cercare un nome in cui "primo" e "secondo" funzionano bene non lo è.
maaartinus,

2

Simple way Object [] - può essere usato come qualsiasi tupla di dimensione


2
Qualsiasi dimensione, sì. Ma: ingombrante da creare e non sicuro per i tipi.
Michael Piefel,

2

Per linguaggi di programmazione come Java, la struttura di dati alternativa utilizzata dalla maggior parte dei programmatori per rappresentare coppie come strutture di dati sono due array e ai dati si accede tramite lo stesso indice

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

Questo non è l'ideale in quanto i dati dovrebbero essere uniti, ma si rivelano anche piuttosto economici. Inoltre, se il tuo caso d'uso richiede l'archiviazione delle coordinate, è meglio costruire la tua struttura dati.

Ho qualcosa del genere nella mia biblioteca

public class Pair<First,Second>{.. }


2

Ecco alcune librerie che hanno più gradi di tuple per comodità:

  • JavaTuples . Le tuple dal grado 1-10 sono tutto ciò che ha.
  • JavaSlang . Tuple dal grado 0-8 e molte altre chicche funzionali.
  • jOOλ . Tuple di grado 0-16 e alcuni altri gadget funzionali. (Dichiarazione di non responsabilità, lavoro per la società di manutenzione)
  • Java funzionale . Tuple dal grado 0-8 e molte altre chicche funzionali.

Altre librerie sono state menzionate per contenere almeno la Pairtupla.

In particolare, nel contesto della programmazione funzionale che utilizza molta tipizzazione strutturale, piuttosto che tipizzazione nominale ( come sostenuto nella risposta accettata ), quelle librerie e le loro tuple sono molto utili.



2

un'altra implementazione lombok concisa

import lombok.Value;

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

1

Ho notato che tutte le implementazioni di Pair disseminate qui attribuiscono un significato all'ordine dei due valori. Quando penso a una coppia, penso a una combinazione di due elementi in cui l'ordine dei due non ha importanza. Ecco la mia implementazione di una coppia non ordinata, con hashCodee equalssostituzioni per garantire il comportamento desiderato nelle raccolte. Clonabile anche.

/**
 * 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;

    }

}

Questa implementazione è stata correttamente testata dall'unità ed è stato provato l'uso in un set e in una mappa.

Nota che non sto affermando di rilasciarlo di dominio pubblico. Questo è il codice che ho appena scritto per l'uso in un'applicazione, quindi se hai intenzione di usarlo, ti preghiamo di astenersi dal fare una copia diretta e fare un po 'casino con i commenti e i nomi. Cattura la mia deriva?


3
in realtà, controlla il fondo di ogni pagina: "contributi degli utenti concessi in licenza sotto cc-wiki"
amara,

Ah, non me ne ero accorto. Grazie per il testa a testa. In tal caso, usa il codice che ritieni opportuno sotto quella licenza.
G_H,

1
La domanda riguarda una coppia equivalente C ++, che è ordinata. Inoltre, penso che fintanto che uno ha un riferimento all'oggetto Pair e quelli sono mutabili, l'inserimento di coppie nelle raccolte potrebbe portare a comportamenti indefiniti.
Mr_and_Mrs_D il


1

com.sun.tools.javac.util.Pair è una semplice implementazione di una coppia. Può essere trovato in jdk1.7.0_51 \ lib \ tools.jar.

A parte org.apache.commons.lang3.tuple.Pair, non è solo un'interfaccia.


2
Nessuno dovrebbe usare le API interne JDK, però.
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;
    }

}

utilizzo:

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

Immutabile, solo un paio!


Oh wow. Un altro? Prova a usare il tuo con generici più complessi: a un certo punto, non riuscirà a inferire i tipi appropriati. Inoltre, dovrebbe essere possibile quanto segue: Pair<Object, Object> pair = Pair.createPair("abc", "def")ma immagino che uno debba scrivere Pair.createPair((Object)"abc", (Object)"def")con il proprio codice?
Ha QUIT - Anony-Mousse il

puoi sostituire il metodo statico con questo: @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); } ma non so se sia una buona pratica
Bastiflew

No, questo probabilmente rovinerà ancora di più le cose. Nella mia esperienza, almeno uno dei compilatori (provare java6, java7, javadoc ed il compilatore java eclipse) si lamenterà. Il tradizionale new Pair<Object, Object>("abc", "def")era il più affidabile nei miei esperimenti.
Ha QUIT - Anony-Mousse il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.