Değer çiftlerinin Java koleksiyonu? (Demetler?)


345

Java'nın bir Haritaya sahip olmasını seviyorum, örneğin haritadaki her bir girişin türlerini tanımlayabilirsiniz. <String, Integer> .

Aradığım şey, koleksiyondaki her öğenin bir çift değer olduğu bir koleksiyon türüdür. Çiftteki her değer, bildirim zamanında tanımlanan kendi türüne (yukarıdaki Dize ve Tamsayı örneği gibi) sahip olabilir.

Koleksiyon, verilen sırasını koruyacak ve değerlerden birine benzersiz bir anahtar (bir haritadaki gibi) gibi davranmayacaktır.

Esasen ARRAY tipinde <String,Integer>veya diğer 2 tipte tanımlamak istiyorum .

İçinde 2 değişken dışında hiçbir şey olmayan bir sınıf yapabileceğimin farkındayım, ama bu aşırı ayrıntılı görünüyor.

Ayrıca bir 2D dizi kullanabileceğimin farkındayım, ancak kullanmam gereken farklı türler nedeniyle, onları OBJECT dizileri yapmak zorunda kalacaktım ve sonra her zaman döküm yapmak zorunda kalacağım.

Koleksiyonda yalnızca çiftleri depolamam gerekiyor, bu yüzden giriş başına sadece iki değere ihtiyacım var. Sınıf rotasına gitmeden böyle bir şey var mı? Teşekkürler!


Guava bunun için de bir sınıf olabilir merak ediyorum.
Sikorski

Guava oldukça anti- Pairve Google'daki insanlar çok daha iyi bir alternatif - Auto / Value - yaratacak kadar ileri gittiler . Uygun equals / hashCode semantiği ile iyi yazılan değer türü sınıfları kolayca oluşturmanızı sağlar . Bir daha asla bir Pairtüre ihtiyacınız olmayacak !
dimo414

Yanıtlar:


255

Pair sınıfı, kendi başınıza yazmak için yeterince kolay olan bu "verim" jeneriklerinden biridir. Örneğin, başımın üstünden:

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

}

Ve evet, bu, Net'in çeşitli yerlerinde, çeşitli derecelerde eksiksizlik ve özellik ile var. (Yukarıdaki örneğimin değişmez olması amaçlanmıştır.)


17
Bunu beğendim, ancak sol ve sağ alanları herkese açık hale getirme hakkında ne düşünüyorsunuz? Pair sınıfının hiçbir zaman ilişkili bir mantığa sahip olmayacağı ve tüm istemcilerin 'sol' ve 'sağa' erişmesi gerekeceği açıktır, o zaman neden bunu kolaylaştırmıyorsunuz?
Yasadışı Programcı

43
Um ... hayır olmazdı. Alanlar son olarak işaretlenir, bu nedenle yeniden atanamazlar. Ve bu güvenli değil çünkü 'sol' ve 'sağ' değiştirilebilir. GetLeft () / getRight () savunma kopyaları (bu durumda işe yaramaz) döndürmedikçe, büyük anlaşmanın ne olduğunu görmüyorum.
Yasadışı Programcı

17
Sağ ve sağ takas durumunda hashCode () işlevinin aynı değeri verdiğini unutmayın. Belki de:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze

68
Eğer doğru anlıyorsam, basit bir çift sınıf açarken bir sözdizimi hatası, bir subpar hashCode yöntemi, boş işaretçi istisnaları, karşılaştırmak için hiçbir yöntem, tasarım soruları ... ve insanlar hala Apache ortaklarında var iken bu sınıfı yaymayı savunuyor. JAR'ı dahil etmek istemiyorsanız, ancak tekerleği yeniden icat etmeyi bırakıyorsanız lütfen kodu kopyalayın!
cquezel

38
Ne demek "kendi başına yazmak için yeterince kolay"? Bu korkunç bir yazılım mühendisliği. MyPair ve SomeoneElsesPair arasında dönüştürmek için N ^ 2 adaptör sınıfları da kendi başlarına yazmak için yeterince kolay kabul ediliyor mu?
djechlin

299

AbstractMap.SimpleEntry

Kolay bunu arıyorsanız:

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

Nasıl doldurabilirsiniz?

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

Bu basitleşir:

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

Ve, bir createEntryyöntemin yardımıyla , ayrıntı düzeyini daha da azaltabilir:

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

Yana ArrayListnihai değil, bir ortaya çıkarmak için sınıflandırma edilebilir ofyöntemi (ve yukarıda belirtilen createEntrysözdizimsel kısa ve öz ile sonuçlanan bir yöntem):

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

11
FYI: Önerilen SimpleEntry, setValuedeğişmez bir kardeş sınıf ihmal yöntemine sahiptir. Böylece adı SimpleImmutableEntry.
Basil Bourque

3
Bu, kendi kendini uygulamadan daha iyidir. Uygulamanın kolay olması, her seferinde uygulamak için bir mazeret değildir.
pevogam

6
Ben "standart" düşünün olmaz. Entryanahtar / değer çifti anlamına gelir ve bunun için de iyidir. Ancak gerçek bir grup olarak zayıflıkları vardır. Örneğin, hashcodeuygulama SimpleEntrysadece öğelerin kodlarını xors olarak gösterir, bu nedenle <a,b>aynı değere sahip olur <b,a>. Tuple uygulamaları sıklıkla sözlükbilimsel olarak sıralanır, ancak SimpleEntry uygulanmaz Comparable. O yüzden dikkatli ol ...
Gene

167

Java 9+

Java 9'da, şunu yazabilirsiniz: Map.entry(key, value) değişmez bir çift oluşturmak için.

Not: Bu yöntem, anahtarların veya değerlerin boş olmasına izin vermez. Eğer boş değerlere izin vermek istiyorsanız, örneğin, bu değiştirmek isteyebilirsiniz: Map.entry(key, Optional.ofNullable(value)).


Java 8+

Java 8'de, javafx.util.Pairdeğiştirilemez, serileştirilebilir bir çift oluşturmak için daha genel amacı kullanabilirsiniz. Bu sınıf gelmez boş tuşları ve boş değerler izin verir. (Java 9'da, bu sınıf javafx.basemodüle dahildir ). DÜZENLEME: Java 11'den itibaren JavaFX, JDK'dan ayrılmıştır, bu nedenle ek maven yapay nesnesi org.openjfx: javafx-base'e ihtiyacınız olacaktır.


Java 6+

Java 6 ve sonraki sürümlerde AbstractMap.SimpleImmutableEntry, değişmez bir çift AbstractMap.SimpleEntryiçin veya değeri değiştirilebilen bir çift için daha ayrıntılı kullanabilirsiniz . Bu sınıflar ayrıca null anahtarlara ve null değerlere izin verir ve serileştirilebilir.


Android

Android için yazıyorsanız, Pair.create(key, value)değişmez bir çift oluşturmak için kullanın .


Apache Commons

Apache Commons LangPair.of(key, value)değişmez, karşılaştırılabilir, serileştirilebilir bir çift oluşturmaya yardımcı olur.


Eclipse Koleksiyonları

İlkel içeren çiftler kullanıyorsanız, Eclipse Collections , tüm verimsiz otomatik boks ve otomatik bokstan kaçınacak bazı çok verimli ilkel çift sınıfları sağlar.

Örneğin, kullanabilirsiniz PrimitiveTuples.pair(int, int)bir yaratmak için IntIntPair, ya PrimitiveTuples.pair(float, long)bir yaratmak FloatLongPair.


Lombok Projesi

Project Lombok'u kullanarak , sadece şunu yazarak değişmez bir çift sınıf oluşturabilirsiniz:

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

Lombok yapıcısı gaz gidericiler, dolduracaktır equals(), hashCode()ve toString()oluşturulan byte sizin için otomatik yöntemlerle. Eğer örneğin statik bir fabrika yöntemini yerine yapıcı, isterseniz bir Pair.of(k, v), sadece Ek açıklamayı değiştirin: @Value(staticConstructor = "of").


Aksi takdirde

Yukarıdaki çözümlerden hiçbiri teknenizi yüzdürmezse, aşağıdaki kodu kopyalayıp yapıştırabilirsiniz (kabul edilen cevapta listelenen sınıfın aksine, NullPointerExceptions'a karşı korur):

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
Ancak JavaFX yalnızca masaüstündedir ve masaüstü olmayan ortamlar (örneğin sunucular) için gereksiz bir bağımlılık ekler.
foo

2
Eclipse Collections ayrıca Pairnesneler için çağrılarak başlatılabilen bir arayüze sahiptir Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
Donald Raab

2
Bu en gerçek Java esque cevabı gibi geliyor. TÜM mevcut uygulamalar. Çok kapsamlı, çok teşekkürler!
Mike

Merhaba @Hans Bir liste kullanıyorum List<Pair<Integer, String> listOfTuple. Nasıl Kullanırım listOfTuple.add()? Yapacaksam - listOfTuple.add(Pair<someInteger, someString>);bu işe yaramaz. Şimdiden teşekkürler.
Me_developer

android sürümü gerçekten yararlı değil yapar birim test, mevcut değil ...
OznOg


31

Apache ortak lang3 Pair sınıf ve bu iş parçacığında bahsedilen diğer birkaç kitaplığı vardır Java C + + Çifti <L, R> eşdeğeri nedir?

Orijinal sorunuzdaki gereksinimi karşılayan örnek:

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
Apache Commons varsa bu en basit seçenektir.
Kip


15

"Apache Commons Lang 3" Pairsınıfı ve ilgili alt sınıflar ne olacak?

    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çiftteki değerlerin değiştirilmesine izin vermeyen belirli bir alt sınıftır, ancak farklı anlamsallığa sahip başka uygulamalar da vardır. Eğer ihtiyacınız varsa, bunlar Maven koordinatlarıdır.

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

11

Genel Pair <A, B> sınıfı yazabilir ve bunu bir dizi veya listede kullanabilirsiniz. Evet, bir sınıf yazmanız gerekiyor, ancak aynı sınıfı tüm türler için yeniden kullanabilirsiniz, böylece yalnızca bir kez yapmanız gerekir.


Bunun bir örneğini görmek isterim!
DivideByHero

1
Dan, bununla ilgili kötü şey, örneğin yalnızca Sil <String, Integer> s türünü silme nedeniyle kabul etmek mümkün değil, değil mi? Java da öyle ...
Johannes Weiss


Bunun düz diziler için işe yarayacağını sanmıyorum, ama kesinlikle diğer Koleksiyonlar için de işe yarayacak.
Yasadışı Programcı

@Outlaw Programmer: İyi bir nokta, dizilerle kullanabilirsiniz, ancak genel diziler oluşturmak için bir kesmek kullanmak zorunda olduğunuz için çirkin.
Dan Dyer

7

Diğer cevapları genişletmek için genel bir değişmez Çifti, kodunuzu yapıcıya çağrı ile karıştırmaktan kaçınmak için statik bir yönteme sahip olmalıdır:

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

statik yöntemi "veya" pairOf "olarak adlandırırsanız, kod aşağıdakilerden birini yazabildiğiniz gibi akıcı hale gelir:

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

çekirdek java kütüphanelerinin bu tür şeylere o kadar seyrek olması gerçekten utanç vericidir. ölçek yükseltmek için başka bir neden ...


6

Ben sadece bir kullanmak istemem eğer soracaktım List<Pair<T, U>>? ancak elbette, JDK'nin bir Pair <> sınıfı yoktur. Ama hızlı bir Google hem bir tane buldum Wikipedia ve forums.sun.com . Şerefe


6

Açıkladığınız gibi tercih edilen çözüm bir Çiftler Listesi'dir (yani Liste).

Bunu yapmak için koleksiyonunuzda kullanmak üzere bir Pair sınıfı oluşturacaksınız. Bu, kod tabanınıza eklemek için kullanışlı bir yardımcı program sınıfıdır.

Sun JDK'da tipik bir Pair sınıfına benzer işlevsellik sağlayan en yakın sınıf AbstractMap.SimpleEntry'dir. Kendi sınıfınızı yaratmak yerine bu sınıfı kullanabilirsiniz, ancak bazı garip kısıtlamalar ile yaşamak zorunda kalacaksınız ve bence çoğu insan bunu SimpleEntry'nin amaçlanan rolü olarak kaşlarını çattı. Örneğin SimpleEntry'de "setKey ()" yöntemi ve varsayılan kurucu yoktur, bu nedenle çok sınırlayıcı bulabilirsiniz.

Koleksiyonların tek bir tür öğeyi içerecek şekilde tasarlandığını unutmayın. Harita gibi ilgili yardımcı arabirimler aslında Koleksiyonlar değildir (yani Harita Koleksiyon arabirimini uygulamaz). Bir Pair de Koleksiyon arayüzünü uygulamaz, ancak daha büyük veri yapılarının oluşturulmasında açık bir şekilde faydalı bir sınıftır.


Bu çözümü, "Pair, <K, V> jenerik çifti ile" kazanan "olandan daha iyi buluyorum. İstenen her şeyi yapar ve kutudan çıkar. Bunu benim uygulama için kullanıyorum.
Sauer

5

Bu JavaHelp4u'nun kodunu temel alır.

Daha az ayrıntılı ve bir satırda nasıl yapılacağını ve bir şeyler üzerinde nasıl döngü yapılacağını gösterir.

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

sadece şöyle bir sınıf yarat

class tuples 
{ 
int x;
int y;
} 

sonra bu tuples nesnelerinin listesi oluşturun

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

böylece diğer yeni veri yapılarını da aynı şekilde uygulayabilirsiniz.


4

Demek istediğim, PairJava'da sınıf olmasa da oldukça benzer şeyler var:Map.Entry

Harita. Giriş Belgeleri

Bu ( HashMapya da biraz basitleştirmek) ne ya da aslında herhangi bir Mapmağaza.

MapDeğerlerinizi içinde saklamak için bir örnek oluşturabilir ve giriş setini alabilirsiniz. Sonunda Set<Map.Entry<K,V>>ne istediğinizi etkili bir şekilde bitireceksiniz .

Yani:

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
}

Bu seçeneğin benzersiz anahtar sorunu var. Diyelim ki kişilerin öznitelikleriniz var (ör. {(Kişi1, "mavi gözlü"), (kişi1, "kızıl saçlı", (kişi2, "dar görüşlü"), (kişi2, "hızlı düşünen")}) Her kişi haritanın anahtarı olduğundan ve kişi başına yalnızca bir özelliğe izin vereceğinden, bunları bir harita olarak çift olarak saklayamazsınız
manuelvigarcia


0

Com.sun.tools.javac.util.Pair hakkında ne?


7
Bu tür tools.jar'dadır - javac derleyicisinin "Sun" uygulamasının bir parçasıdır. Yalnızca JDK'nın bir parçası olarak dağıtılır, diğer satıcı uygulamalarında bulunmayabilir ve genel API'nın bir parçası olması pek olası değildir.
McDowell

0

Anahtar / değer çiftleri hakkında konuşurken aklımdaki ilk şey, öğeleri bir akışa / dosyaya kaydedebileceğiniz ve yükleyebileceğiniz Özellikler Sınıfıdır .


0

Reactor projesinde (io.projectreactor: reaktör çekirdeği) n-Tuples için gelişmiş destek var:

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

Orada t.getT1(), t.getT2(), ...özellikle Stream veya Flux ile grup öğelerini bile haritalandırabilirsiniz:

Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.