Una raccolta Java di coppie di valori? (tuple?)


345

Mi piace come Java ha una mappa in cui è possibile definire i tipi di ogni voce nella mappa, ad esempio <String, Integer>.

Quello che sto cercando è un tipo di raccolta in cui ogni elemento della raccolta è una coppia di valori. Ogni valore nella coppia può avere il proprio tipo (come nell'esempio String e Integer sopra), che viene definito al momento della dichiarazione.

La raccolta manterrà il suo ordine dato e non tratterà uno dei valori come una chiave univoca (come in una mappa).

In sostanza voglio essere in grado di definire un ARRAY di tipo <String,Integer>o qualsiasi altro 2 tipi.

Mi rendo conto di poter creare una classe con nient'altro che le 2 variabili al suo interno, ma questo sembra eccessivamente prolisso.

Mi rendo anche conto che avrei potuto usare un array 2D, ma a causa dei diversi tipi che ho bisogno di usare, dovrei renderli array di OBJECT, e quindi dovrei eseguire il cast tutto il tempo.

Devo solo memorizzare coppie nella raccolta, quindi ho bisogno solo di due valori per voce. Esiste qualcosa del genere senza seguire il percorso di classe? Grazie!


mi chiedo che Guava potrebbe avere una lezione anche per questo.
Sikorski,

Guava è piuttosto anti- Paire la gente di Google è arrivata al punto di creare un'alternativa molto migliore: Auto / Valore . Ti consente di creare facilmente classi di tipi di valore ben tipizzate , con la semantica uguale a uguale / hashCode. Non avrai mai più bisogno di un Pairtipo!
dimo414,

Risposte:


255

La classe Pair è uno di quegli esempi generici "dammi" che è abbastanza facile da scrivere da soli. Ad esempio, dalla parte superiore della mia testa:

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

}

E sì, questo esiste in più punti della Rete, con vari gradi di completezza e funzionalità. (Il mio esempio sopra è destinato a essere immutabile.)


17
Mi piace questo, ma cosa pensi di rendere pubblici i campi sinistro e destro? È abbastanza chiaro che alla classe Pair non sarà mai associata alcuna logica e tutti i client avranno bisogno di accedere a "left" e "right", quindi perché non renderlo semplice?
Programmatore fuorilegge il

43
Um ... no non lo sarebbe. I campi sono contrassegnati come finali, quindi non possono essere riassegnati. E non è sicuro per i thread perché 'left' e 'right' potrebbero essere mutabili. A meno che getLeft () / getRight () non abbia restituito copie difensive (inutile in questo caso), non vedo quale sia il grosso problema.
Programmatore fuorilegge il

17
Si noti che hashCode () così com'è fornisce lo stesso valore se si scambiano sinistra e destra. Forse:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze,

68
Quindi, se ho capito bene, la distribuzione di una semplice classe di coppia produce un errore di sintassi, un metodo hashCode sottoparte, eccezioni puntatore nullo, nessun metodo di confronto, domande di progettazione ... e la gente continua a sostenere la diffusione di questa classe mentre esiste nei beni comuni di Apache. Per favore, copia il codice se non vuoi includere il JAR ma smetti di reinventare la ruota!
cquezel,

38
Cosa intendi con "abbastanza facile da scrivere da solo"? Questa è una terribile ingegneria del software. Le classi di adattatori N ^ 2 da convertire tra MyPair e SomeoneElsesPair sono considerate abbastanza facili da scrivere da sole?
Djechlin,

299

AbstractMap.SimpleEntry

Facile stai cercando questo:

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

Come puoi riempirlo?

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

Questo semplifica:

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

E, con l'aiuto di un createEntrymetodo, è possibile ridurre ulteriormente la verbosità a:

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

Poiché ArrayListnon è definitivo, può essere suddiviso in sottoclassi per esporre un ofmetodo (e il createEntrymetodo sopra citato ), risultando nel sintatticamente conciso:

TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);

11
A proposito: quello suggerito SimpleEntryha una classe di fratelli che omette il setValuemetodo per essere immutabile. Da qui il nome SimpleImmutableEntry.
Basil Bourque,

3
Questo è meglio dell'auto-implementazione. Il fatto che sia facile da implementare non è una scusa per implementarlo ogni volta.
pevogam,

6
Non lo considero "standard". Entryè pensato per essere una coppia chiave-valore, e va bene per quello. Ma ha dei punti deboli come una vera tupla. Ad esempio l' hashcodeimplementazione in SimpleEntryxorizza i codici degli elementi, quindi gli <a,b>hash hanno lo stesso valore di <b,a>. Le implementazioni delle tuple spesso ordinano lessicograficamente, ma SimpleEntry non implementano Comparable. Quindi fai attenzione là fuori ...
Gene,

167

Java 9+

In Java 9, puoi semplicemente scrivere: Map.entry(key, value) per creare una coppia immutabile.

Nota: questo metodo non consente che chiavi o valori siano nulli. Se si desidera consentire valori nulli, per esempio, che ci si vuole cambiare questo a: Map.entry(key, Optional.ofNullable(value)).


Java 8+

In Java 8, è possibile utilizzare lo scopo più generale javafx.util.Pairper creare una coppia immutabile e serializzabile. Questa classe non permette chiavi nulli e valori nulli. (In Java 9, questa classe è inclusa nel javafx.basemodulo). EDIT: A partire da Java 11, JavaFX è stato disaccoppiato dal JDK, quindi avresti bisogno del manufatto aggiuntivo org.openjfx: javafx-base.


Java 6+

In Java 6 e versioni successive, è possibile utilizzare il più dettagliato AbstractMap.SimpleImmutableEntryper una coppia immutabile o AbstractMap.SimpleEntryper una coppia il cui valore può essere modificato. Queste classi consentono anche chiavi null e valori null e sono serializzabili.


androide

Se stai scrivendo per Android, basta usare Pair.create(key, value)per creare una coppia immutabile.


Apache Commons

Apache Commons Langfornisce l'utile Pair.of(key, value)per creare una coppia immutabile, comparabile, serializzabile.


Collezioni Eclipse

Se stai usando coppie che contengono primitive, Eclipse Collections fornisce alcune classi di coppie di primitive molto efficienti che eviteranno tutti gli inefficienti box-auto e auto-unboxing.

Ad esempio, è possibile utilizzare PrimitiveTuples.pair(int, int)per creare unIntIntPair o PrimitiveTuples.pair(float, long)per creare un FloatLongPair.


Progetto Lombok

utilizzando Project Lombok , puoi creare una classe di coppia immutabile semplicemente scrivendo:

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

Lombok riempirà nel costruttore, getter, equals(),hashCode() , e toString()metodi per voi automaticamente nel bytecode generato. Se si desidera un metodo factory statico invece di un costruttore, ad esempio, una Pair.of(k, v), è sufficiente modificare l'annotazione a: @Value(staticConstructor = "of").


Altrimenti

Se nessuna delle soluzioni sopra indicate fa galleggiare la tua barca, puoi semplicemente copiare e incollare il seguente codice (che, a differenza della classe elencata nella risposta accettata, protegge da 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;
    }
}

3
JavaFX è tuttavia solo desktop, aggiungendo una dipendenza non necessaria per ambienti non desktop (ad es. Server).
foo,

2
Eclipse Collections ha anche Pairun'interfaccia per gli oggetti, che può essere istanziata chiamando Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
Donald Raab,

2
Questa sembra la risposta più autentica di Java. TUTTE le implementazioni disponibili. Molto approfondito, molte grazie!
Mike,

Ciao @Hans, sto usando un elenco List<Pair<Integer, String> listOfTuple. Come si usa listOfTuple.add()? Se lo farò listOfTuple.add(Pair<someInteger, someString>);, questo non funzionerà. Grazie in anticipo.
Me_developer

la versione Android non è disponibile nel test unitario, il che la rende non molto utile ...
OznOg


31

Apache common lang3 ha classe Pair e poche altre librerie menzionate in questo thread Qual è l'equivalente della coppia C ++ <L, R> in Java?

Esempio corrispondente al requisito della domanda originale:

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

1
Questa è l'opzione più semplice se è disponibile Apache Commons.
Kip


15

A proposito di "Apache Commons Lang 3" Pairdi classe e le relative sottoclassi?

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

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

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

ImmutablePairè una sottoclasse specifica che non consente di modificare i valori nella coppia, ma esistono altre implementazioni con semantica diversa. Queste sono le coordinate Maven, se ne hai bisogno.

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

11

È possibile scrivere una classe Pair <A, B> generica e utilizzarla in una matrice o in un elenco. Sì, devi scrivere una classe, ma puoi riutilizzare la stessa classe per tutti i tipi, quindi devi farlo solo una volta.


Mi piacerebbe vederne un esempio!
DivideByHero,

1
Dan, la cosa brutta è che non è possibile accettare, ad esempio, solo Pair <String, Integer> s a causa della cancellazione del tipo, no? Ma anche Java ...
Johannes Weiss,


Non penso che funzionerà con array semplici, ma sicuramente funzionerà con altre Collezioni.
Programmatore fuorilegge il

@Outlaw Programmer: buon punto, puoi usarlo con gli array, ma è brutto perché devi usare un hack per creare array generici.
Dan Dyer,

7

Espandendo sulle altre risposte una coppia immutabile generica dovrebbe avere un metodo statico per evitare di ingombrare il codice con la chiamata al costruttore:

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

se chiami il metodo statico "of" o "pairOf" il codice diventa fluente in quanto puoi scrivere:

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

è un vero peccato che le librerie core Java siano così scarne su tali cose che devi usare commons-lang o altre terze parti per fare cose così basilari. ancora un altro motivo per passare a scala ...


6

Stavo per chiedere se non vorresti usare solo un List<Pair<T, U>>? ma poi, ovviamente, il JDK non ha una classe Pair <>. Ma un rapido Google ne ha trovato uno sia su Wikipedia che su forums.sun.com . Saluti


6

La soluzione preferita come è stata descritta è un Elenco di coppie (ovvero Elenco).

Per fare ciò, devi creare una classe Pair da utilizzare nella tua raccolta. Questa è un'utile classe di utilità da aggiungere alla tua base di codice.

La classe più vicina in Sun JDK che fornisce funzionalità simili a una tipica classe Pair è AbstractMap.SimpleEntry. Potresti usare questa classe piuttosto che creare la tua classe Pair, anche se dovresti convivere con alcune restrizioni imbarazzanti e penso che la maggior parte delle persone si acciglierebbe su questo come non proprio il ruolo previsto di SimpleEntry. Ad esempio SimpleEntry non ha un metodo "setKey ()" e nessun costruttore predefinito, quindi potresti trovarlo troppo limitante.

Tieni presente che le raccolte sono progettate per contenere elementi di un solo tipo. Le interfacce di utilità correlate come Map non sono effettivamente raccolte (ovvero Map non implementa l'interfaccia di raccolta). Una coppia non implementerebbe neanche l'interfaccia Collection ma è ovviamente una classe utile nella costruzione di strutture dati più grandi.


Trovo questa soluzione molto migliore di quella "vincente" con la coppia generica <K, V>. Fa tutto il necessario e viene fuori dalla scatola. Uso questo per la mia implementazione.
Sauer,

5

Questo si basa sul codice JavaHelp4u.

Meno dettagliato e mostra come fare in una riga e come fare il giro delle cose.

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


4

basta creare una classe come

class tuples 
{ 
int x;
int y;
} 

quindi crea l'elenco di questi oggetti di tuple

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

così puoi anche implementare altre nuove strutture dati allo stesso modo.


4

Voglio dire, anche se non esiste una Pairclasse in Java, c'è qualcosa di abbastanza simile:Map.Entry

Documentazione Map.Entry

Questo è (semplificando un po ') cosa HashMap, o in realtà qualsiasi Mapnegozio.

È possibile creare un'istanza in cui Mapmemorizzare i propri valori e ottenere il set di voci. Si finirà con un Set<Map.Entry<K,V>>che è effettivamente quello che vuoi.

Così:

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
}

Questa opzione presenta il problema delle chiavi univoche. Supponiamo che tu abbia gli attributi di persone (es. {(Person1, "blue-eyed"), (person1, "red-haired", (person2, "miope"), (person2, "quick-thinker")}) non essere in grado di memorizzarli come coppie in una mappa, poiché ogni persona è la chiave della mappa e consentirebbe solo un attributo a persona.
manuelvigarcia,


0

Che dire di com.sun.tools.javac.util.Pair?


7
Questo tipo è in tools.jar - parte dell'implementazione "Sun" del compilatore javac. È distribuito solo come parte di JDK, potrebbe non essere presente nelle implementazioni di altri fornitori ed è improbabile che faccia parte dell'API pubblica.
McDowell,

0

La prima cosa che mi viene in mente quando si parla di coppie chiave / valore è la classe Proprietà in cui è possibile salvare e caricare elementi in un flusso / file.


0

Nel progetto Reactor (io.projectreactor: reattore-core) c'è un supporto avanzato per n-Tuple:

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

Lì puoi ottenere t.getT1(), t.getT2(), ...Soprattutto con Stream o Flux puoi persino mappare gli elementi tupla:

Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));
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.