Kumpulan pasangan nilai Java? (tupel?)


345

Saya suka bagaimana Java memiliki Peta di mana Anda dapat menentukan jenis setiap entri di peta, misalnya <String, Integer>.

Apa yang saya cari adalah jenis koleksi di mana setiap elemen dalam koleksi adalah sepasang nilai. Setiap nilai dalam pasangan dapat memiliki jenisnya sendiri (seperti contoh String dan Integer di atas), yang didefinisikan pada waktu deklarasi.

Koleksi akan mempertahankan pesanannya dan tidak akan memperlakukan salah satu nilai sebagai kunci unik (seperti di peta).

Pada dasarnya saya ingin dapat mendefinisikan jenis ARRAY <String,Integer>atau 2 jenis lainnya.

Saya menyadari bahwa saya dapat membuat kelas dengan apa-apa selain 2 variabel di dalamnya, tetapi itu tampaknya terlalu bertele-tele.

Saya juga menyadari bahwa saya bisa menggunakan array 2D, tetapi karena berbagai jenis yang perlu saya gunakan, saya harus membuat mereka array OBJECT, dan kemudian saya harus membuang semua waktu.

Saya hanya perlu menyimpan pasangan dalam koleksi, jadi saya hanya perlu dua nilai per entri. Apakah ada yang seperti ini ada tanpa melewati rute kelas? Terima kasih!


Saya heran Guava mungkin memiliki kelas untuk ini juga.
Sikorski

Guava sangat anti- Pair, dan orang-orang di Google sejauh ini telah menciptakan alternatif yang jauh lebih baik - Otomatis / Nilai . Ini memungkinkan Anda dengan mudah membuat kelas tipe nilai yang diketik dengan baik, dengan semantik yang sama dengan / kode hash. Anda tidak akan pernah perlu Pairmengetik lagi!
dimo414

Jawaban:


255

Kelas Pair adalah salah satu contoh generik "beri" yang cukup mudah untuk ditulis sendiri. Misalnya, dari atas kepala saya:

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

}

Dan ya, ini ada di banyak tempat di Internet, dengan berbagai tingkat kelengkapan dan fitur. (Contoh saya di atas dimaksudkan untuk tidak berubah.)


17
Saya suka ini, tetapi apa yang Anda pikirkan tentang membuat bidang kiri dan kanan menjadi publik? Sudah cukup jelas bahwa kelas Pair tidak akan pernah memiliki logika yang terkait dan semua klien akan membutuhkan akses ke 'kiri' dan 'benar,' jadi mengapa tidak membuatnya mudah?
Outlaw Programmer

43
Um ... tidak, itu tidak akan terjadi. Bidang ditandai sebagai final, sehingga tidak dapat dipindahkan. Dan itu bukan threadsafe karena 'kiri' dan 'kanan' bisa berubah. Kecuali getLeft () / getRight () mengembalikan salinan defensif (tidak berguna dalam kasus ini), saya tidak melihat apa masalahnya.
Outlaw Programmer

17
Perhatikan bahwa kode hash () apa adanya memberikan nilai yang sama jika kiri dan kanan bertukar. Mungkin:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze

68
Jadi jika saya mengerti dengan benar, meluncurkan kelas pasangan sederhana menghasilkan kesalahan sintaks, metode kode hash di bawah standar, pengecualian null pointer, tidak ada metode ToTo, pertanyaan desain ... dan orang-orang masih menganjurkan meluncurkan kelas ini saat ada di Apache commons. Tolong, cukup salin kode jika Anda tidak ingin memasukkan JAR tetapi berhenti menciptakan kembali roda!
cquezel

38
Apa maksud Anda "cukup mudah untuk menulis sendiri"? Itu rekayasa perangkat lunak yang mengerikan . Apakah kelas adaptor N ^ 2 untuk mengkonversi antara MyPair dan SomeoneElsesPair juga dianggap cukup mudah untuk ditulis sendiri?
djechlin

299

AbstractMap.SimpleEntry

Mudah Anda mencari ini:

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

Bagaimana Anda bisa mengisinya?

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

Ini menyederhanakan untuk:

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

Dan, dengan bantuan createEntrymetode, selanjutnya dapat mengurangi verbositas menjadi:

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

Karena ArrayListtidak final, maka dapat disubklasifikasikan untuk mengekspos suatu ofmetode (dan createEntrymetode yang disebutkan sebelumnya ), menghasilkan secara sintaksis singkat:

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

11
FYI: Itu menyarankan SimpleEntrymemiliki setValuemetode menghilangkan kelas saudara kandung agar tidak berubah. Demikian namanya SimpleImmutableEntry.
Basil Bourque

3
Ini lebih baik daripada implementasi mandiri. Fakta bahwa penerapannya mudah bukanlah alasan untuk mengimplementasikannya setiap kali.
pevogam

6
Saya tidak akan menganggapnya "standar." Entrydimaksudkan untuk menjadi pasangan nilai kunci, dan tidak apa-apa untuk itu. Tetapi memiliki kelemahan sebagai tuple sejati. Sebagai contoh hashcodeimplementasi SimpleEntryhanya xor kode elemen, jadi <a,b>hash dengan nilai yang sama dengan <b,a>. Implementasi Tuple sering diurutkan secara leksikografis, tetapi SimpleEntry tidak diimplementasikan Comparable. Jadi berhati-hatilah di luar sana ...
Gene

167

Java 9+

Di Java 9, Anda cukup menulis: Map.entry(key, value) untuk membuat pasangan yang tidak dapat diubah.

Catatan: metode ini tidak mengizinkan kunci atau nilai menjadi nol. Jika Anda ingin mengizinkan nilai null, misalnya, Anda ingin mengubahnya ke: Map.entry(key, Optional.ofNullable(value)).


Java 8+

Di Java 8, Anda bisa menggunakan tujuan yang lebih umum javafx.util.Pairuntuk membuat pasangan serialable yang tidak berubah. Kelas ini mengizinkan kunci nol dan nilai nol. (Di Java 9, kelas ini termasuk dalam javafx.basemodul). EDIT: Pada Java 11, JavaFX telah dipisahkan dari JDK, jadi Anda akan memerlukan artefak pakar tambahan org.openjfx: javafx-base.


Java 6+

Di Java 6 dan lebih tinggi, Anda bisa menggunakan lebih banyak verbose AbstractMap.SimpleImmutableEntryuntuk pasangan yang tidak dapat diubah, atau AbstractMap.SimpleEntryuntuk pasangan yang nilainya dapat diubah. Kelas-kelas ini juga memungkinkan kunci nol dan nilai nol, dan dapat serial.


Android

Jika Anda menulis untuk Android, cukup gunakan Pair.create(key, value)untuk membuat pasangan yang tidak dapat diubah.


Apache Commons

Apache Commons Langmemberikan bantuan Pair.of(key, value)untuk membuat pasangan yang berubah, sebanding, serializable.


Koleksi Eclipse

Jika Anda menggunakan pasangan yang mengandung primitif, Eclipse Collections menyediakan beberapa kelas pasangan primitif yang sangat efisien yang akan menghindari semua kotak-otomatis dan kotak-otomatis yang tidak efisien.

Misalnya, Anda dapat menggunakan PrimitiveTuples.pair(int, int)untuk membuat IntIntPair, atau PrimitiveTuples.pair(float, long)untuk membuat FloatLongPair.


Proyek Lombok

Menggunakan Project Lombok , Anda dapat membuat kelas pasangan tetap dengan menulis:

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

Lombok akan mengisi konstruktor, getter, equals(), hashCode(), dan toString()metode untuk Anda secara otomatis dalam bytecode yang dihasilkan. Jika Anda ingin metode pabrik statis bukan konstruktor, misalnya, Pair.of(k, v), hanya mengubah anotasi ke: @Value(staticConstructor = "of").


Jika tidak

Jika tidak ada solusi di atas yang mengapung perahu Anda, Anda dapat dengan mudah menyalin dan menempelkan kode berikut (yang, tidak seperti kelas yang tercantum dalam jawaban yang diterima, penjaga terhadap 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
Namun JavaFX hanya untuk desktop, menambahkan ketergantungan yang tidak perlu untuk lingkungan non-desktop (mis. Server).
foo

2
Eclipse Collections juga memiliki Pairantarmuka untuk objek, yang dapat dipakai dengan memanggil Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
Donald Raab

2
Ini terasa seperti jawaban Jawa-esque yang paling benar. SEMUA implementasi tersedia. Sangat teliti, terima kasih banyak!
Mike

Hai @Hans saya menggunakan daftar List<Pair<Integer, String> listOfTuple. Bagaimana saya menggunakan listOfTuple.add()? Jika saya akan melakukan - listOfTuple.add(Pair<someInteger, someString>);, ini tidak akan berhasil. Terima kasih sebelumnya.
Me_developer

versi android tidak tersedia dalam unit test, yang membuatnya tidak terlalu berguna ...
OznOg


31

Apache common lang3 memiliki kelas Pair dan beberapa perpustakaan lain yang disebutkan di utas ini. Apa yang setara dengan C ++ Pair <L, R> di Jawa?

Contoh yang cocok dengan persyaratan dari pertanyaan awal Anda:

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
Ini adalah opsi paling sederhana jika Apache Commons tersedia.
Kip


15

Bagaimana dengan kelas "Apache Commons Lang 3" Pairdan subclass relatif?

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

ImmutablePairadalah subclass spesifik yang tidak memungkinkan nilai-nilai dalam pasangan untuk dimodifikasi, tetapi ada implementasi lain dengan semantik yang berbeda. Ini adalah koordinat Maven, jika Anda membutuhkannya.

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

11

Anda bisa menulis kelas Pair generik <A, B> dan menggunakannya dalam array atau daftar. Ya, Anda harus menulis kelas, tetapi Anda dapat menggunakan kembali kelas yang sama untuk semua jenis, jadi Anda hanya perlu melakukannya sekali.


Saya ingin melihat contohnya!
DivideByHero

1
Dan, hal buruk tentang itu adalah bahwa tidak mungkin untuk menerima mis. Hanya Pair <String, Integer> s karena tipe erasure, bukan? Tapi begitu Java ...
Johannes Weiss


Saya tidak berpikir ini akan berfungsi untuk array sederhana, tetapi pasti akan bekerja untuk Koleksi lainnya.
Outlaw Programmer

@Outlaw Programmer: Poin bagus, Anda dapat menggunakannya dengan array, tetapi jelek karena Anda harus menggunakan hack untuk membuat array generik.
Dan Dyer

7

Memperluas jawaban lain, Pasangan tidak berubah yang generik harus memiliki metode statis untuk menghindari mengacaukan kode Anda dengan panggilan ke konstruktor:

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

jika Anda memberi nama metode statis "of" atau "pairOf" kode menjadi lancar ketika Anda dapat menulis:

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

sungguh memalukan bahwa perpustakaan inti java sangat jarang pada hal-hal seperti itu sehingga Anda harus menggunakan commons-lang atau pihak ke-3 lainnya untuk melakukan hal-hal dasar tersebut. namun alasan lain untuk meningkatkan ke scala ...


6

Saya akan bertanya apakah Anda tidak ingin hanya menggunakan List<Pair<T, U>>? tapi kemudian, tentu saja, JDK tidak memiliki kelas Pair <>. Tetapi Google cepat menemukan satu di Wikipedia , dan forums.sun.com . Bersulang


6

Solusi yang disukai seperti yang telah Anda gambarkan adalah Daftar Pasangan (yaitu Daftar).

Untuk mencapai ini, Anda akan membuat kelas Pair untuk digunakan dalam koleksi Anda. Ini adalah kelas utilitas yang berguna untuk ditambahkan ke basis kode Anda.

Kelas terdekat di Sun JDK menyediakan fungsionalitas yang mirip dengan kelas Pair khas adalah AbstractMap.SimpleEntry. Anda dapat menggunakan kelas ini daripada membuat kelas Pair Anda sendiri, meskipun Anda harus hidup dengan beberapa pembatasan yang canggung dan saya pikir sebagian besar orang akan tidak menyukai ini karena sebenarnya bukan peran yang dimaksudkan oleh SimpleEntry. Misalnya SimpleEntry tidak memiliki metode "setKey ()" dan tidak ada konstruktor default, jadi Anda mungkin merasa terlalu membatasi.

Ingatlah bahwa Koleksi dirancang untuk mengandung elemen-elemen dari satu jenis. Antarmuka utilitas terkait seperti Map sebenarnya bukan Koleksi (mis. Peta tidak mengimplementasikan antarmuka Koleksi). Sepasang tidak akan mengimplementasikan antarmuka Koleksi tetapi jelas kelas yang berguna dalam membangun struktur data yang lebih besar.


Saya menemukan solusi ini jauh lebih baik daripada yang "menang" dengan Pair generik <K, V>. Itu melakukan semua yang diminta dan keluar dari kotak. Saya menggunakan ini untuk implementasi saya.
Sauer

5

Ini didasarkan pada kode JavaHelp4u.

Kurang bertele-tele dan tunjukkan bagaimana melakukannya dalam satu baris dan bagaimana melompati banyak hal.

//======>  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

buat saja kelas seperti

class tuples 
{ 
int x;
int y;
} 

lalu buat Daftar objek tupel ini

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

jadi Anda juga bisa menerapkan struktur data baru lainnya dengan cara yang sama.


4

Maksudku, meskipun tidak ada Pairkelas di Jawa ada sesuatu yang sangat mirip:Map.Entry

Peta. Dokumentasi Masuk

Ini (menyederhanakan sedikit) apa HashMap, atau sebenarnya Maptoko apa pun .

Anda bisa membuat instance Mapmenyimpan nilai-nilai Anda di dalamnya dan mendapatkan set entri. Anda akan mendapatkan Set<Map.Entry<K,V>>apa yang Anda inginkan secara efektif.

Begitu:

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
}

Opsi ini memiliki masalah kunci unik. Katakanlah Anda memiliki atribut orang (mis. {(Person1, "bermata biru"), (person1, "berambut merah", (person2, "rabun dekat"), (person2, "pemikir cepat")}) Anda akan tidak dapat menyimpannya sebagai pasangan di peta, karena setiap orang adalah kunci peta dan hanya akan mengizinkan satu atribut per orang
manuelvigarcia


0

Bagaimana dengan com.sun.tools.javac.util.Pair?


7
Tipe ini ada di tools.jar - bagian dari implementasi "Sun" dari kompiler javac. Ini hanya didistribusikan sebagai bagian dari JDK, mungkin tidak ada dalam implementasi vendor lain dan tidak mungkin menjadi bagian dari API publik.
McDowell

0

Hal pertama yang saya pikirkan ketika berbicara tentang pasangan kunci / nilai adalah Kelas Properti tempat Anda dapat menyimpan dan memuat item ke aliran / file.


0

Dalam Reaktor proyek (io.projectreactor: reaktor-inti) ada dukungan lanjutan untuk n-Tuples:

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

Di sana Anda bisa mendapatkan t.getT1(), t.getT2(), ...Terutama dengan Stream atau Flux Anda bahkan dapat memetakan elemen tuple:

Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.