Zbiór par wartości Java? (krotki?)

345

Podoba mi się, w jaki sposób Java ma mapę, w której można na przykład definiować typy każdego wpisu na mapie <String, Integer>.

To, czego szukam, to rodzaj kolekcji, w której każdy element w kolekcji jest parą wartości. Każda wartość w parze może mieć swój własny typ (jak powyższy przykład Ciąg i Liczba całkowita), który jest definiowany w czasie deklaracji.

Kolekcja zachowa podaną kolejność i nie będzie traktować jednej z wartości jako unikalnego klucza (jak na mapie).

Zasadniczo chcę być w stanie zdefiniować tablicę typu <String,Integer>lub dowolne inne 2 typy.

Zdaję sobie sprawę, że mogę stworzyć klasę, w której są tylko 2 zmienne, ale wydaje się to zbyt szczegółowe.

Zdaję sobie również sprawę, że mógłbym użyć tablicy 2D, ale z powodu różnych typów, których muszę używać, musiałbym zrobić z nich tablice OBJECT, a potem musiałbym rzucać cały czas.

Muszę tylko przechowywać pary w kolekcji, więc potrzebuję tylko dwóch wartości na pozycję. Czy coś takiego istnieje bez wybrania się na trasę klasową? Dzięki!

DivideByHero
źródło
Zastanawiam się, że Guava może mieć do tego również klasę.
Sikorski
Guawa jest dość anty- Pair, a ludzie w Google posunęli się do stworzenia znacznie lepszej alternatywy - Auto / Value . Pozwala łatwo tworzyć dobrze typowane klasy typów wartości, z odpowiednią semantyką equals / hashCode. Nigdy więcej nie będziesz potrzebować Pairtypu!
dimo414,

Odpowiedzi:

255

Klasa Pair jest jednym z tych ogólnych przykładów, które można łatwo napisać samodzielnie. Na przykład z czubka głowy:

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

}

I tak, istnieje to w wielu miejscach w sieci, z różnym stopniem kompletności i funkcjonalności. (Mój przykład powyżej ma być niezmienny).

Paul Brinkley
źródło
17
Podoba mi się to, ale co sądzisz o upublicznieniu lewego i prawego pola? Jest całkiem jasne, że do klasy Pair nigdy nie będzie przypisana żadna logika i wszyscy klienci będą potrzebowali dostępu do „lewej” i „prawej”, więc dlaczego by nie ułatwić?
Outlaw Programmer
43
Um ... nie, nie zrobiłby tego. Pola są oznaczone jako ostateczne, więc nie można ich ponownie przypisać. I to nie jest bezpieczne dla wątków, ponieważ „lewo” i „prawo” mogą być zmienne. O ile getLeft () / getRight () nie zwróci kopii obronnych (w tym przypadku bezużyteczne), nie rozumiem o co chodzi.
Outlaw Programmer
17
Zauważ, że hashCode () as-is daje tę samą wartość, jeśli lewy i prawy zostaną zamienione. Być może:long l = left.hashCode() * 2654435761L; return (int)l + (int)(l >>> 32) + right.hashCode();
karmakaze
68
Więc jeśli dobrze rozumiem, wprowadzenie prostej klasy parowej powoduje błąd składniowy, metodę akapitową hashCode, wyjątki wskaźnika zerowego, brak metody porównania do metody, pytania projektowe ... a ludzie nadal opowiadają się za rozwijaniem tej klasy, dopóki jest ona dostępna we wspólnym Apache. Po prostu skopiuj kod, jeśli nie chcesz dołączać JAR, ale przestań wymyślać koło!
cquezel
38
Co masz na myśli „dość łatwo pisać na własną rękę”? To okropna inżynieria oprogramowania. Czy klasy adapterów N ^ 2 do konwersji między MyPair i SomeoneElsesPair są również uważane za wystarczająco łatwe do pisania we własnym zakresie?
djechlin
299

AbstractMap.SimpleEntry

Łatwo tego szukasz:

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

Jak możesz to wypełnić?

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

Upraszcza to:

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

I za pomocą createEntrymetody może dodatkowo zmniejszyć gadatliwość w celu:

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

Ponieważ ArrayListnie jest ostateczny, można go podklasować, aby ujawnić ofmetodę (i wyżej wspomnianą createEntrymetodę), co daje zwięzłą składnię:

TupleList<java.util.Map.Entry<String,Integer>> pair = new TupleList<>();
pair.of("Not Unique key1", 1);
pair.of("Not Unique key2", 2);
JavaHelp4u
źródło
11
FYI: To sugeruje, SimpleEntryże istnieje setValuemetoda pomijania klasy rodzeństwa , która jest niezmienna. Tak więc nazwa SimpleImmutableEntry.
Basil Bourque,
3
To lepsze niż samorealizacja. Fakt, że jest łatwy do wdrożenia, nie usprawiedliwia go za każdym razem.
pevogam
6
Nie uważałbym tego za „standardowe”. Entryma być parą klucz-wartość i jest w porządku. Ale ma słabości jako prawdziwa krotka. Na przykład hashcodeimplementacja SimpleEntrytylko xors kodów elementów, więc <a,b>hash do tej samej wartości co <b,a>. Implementacje krotki często sortują leksykograficznie, ale SimpleEntry nie implementują Comparable. Bądź więc ostrożny ...
Gene,
167

Java 9+

W Javie 9 możesz po prostu napisać: Map.entry(key, value) aby stworzyć niezmienną parę.

Uwaga: ta metoda nie pozwala, aby klucze lub wartości były zerowe. Jeśli chcesz zezwolić na wartości null, na przykład, że chcesz to zmienić na: Map.entry(key, Optional.ofNullable(value)).


Java 8+

W Javie 8 możesz użyć bardziej ogólnego przeznaczenia, javafx.util.Pairaby stworzyć niezmienną, szeregowalną parę. Klasa ta ma umożliwić klucze nieważne i wartości null. (W Javie 9 ta klasa jest zawarta w javafx.basemodule). EDYCJA: Od wersji Java 11 JavaFX został oddzielony od JDK, więc potrzebujesz dodatkowego artefaktu maven org.openjfx: javafx-base.


Java 6+

W Javie 6 i nowszych możesz użyć bardziej szczegółowego AbstractMap.SimpleImmutableEntrydla niezmiennej pary lub AbstractMap.SimpleEntrydla pary, której wartość można zmienić. Klasy te zezwalają również na klucze zerowe i wartości zerowe oraz są serializowane.


Android

Jeśli piszesz dla Androida, po prostu użyj, Pair.create(key, value)aby stworzyć niezmienną parę.


Apache Commons

Apache Commons Langzapewnia pomoc w Pair.of(key, value)tworzeniu niezmiennej, porównywalnej, możliwej do serializacji pary.


Kolekcje Eclipse

Jeśli używasz par zawierających prymitywy, Eclipse Collections zapewnia kilka bardzo wydajnych klas par prymitywnych, które pozwolą uniknąć wszystkich nieefektywnych auto-boxowania i auto-rozpakowywania.

Na przykład, można użyć PrimitiveTuples.pair(int, int), aby utworzyć IntIntPair, lub PrimitiveTuples.pair(float, long)stworzyć FloatLongPair.


Projekt Lombok

Korzystając z Project Lombok , możesz stworzyć niezmienną klasę par, pisząc:

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

Lombok wypełni w konstruktorze, pochłaniacze equals(), hashCode()oraz toString()metod dla Ciebie automatycznie w wygenerowanym kodu bajtowego. Jeśli chcesz statyczne metody fabryki zamiast konstruktora, np Pair.of(k, v), wystarczy zmienić adnotacji do: @Value(staticConstructor = "of").


Inaczej

Jeśli żadne z powyższych rozwiązań nie unosi twojej łodzi, możesz po prostu skopiować i wkleić następujący kod (który, w przeciwieństwie do klasy wymienionej w zaakceptowanej odpowiedzi, chroni przed wyjątkami 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;
    }
}
Hans Brende
źródło
3
JavaFX jest jednak dostępny tylko dla komputerów stacjonarnych, co dodaje niepotrzebną zależność w środowiskach innych niż stacjonarne (np. Serwery).
foo
2
Eclipse Collections ma również Pairinterfejs dla obiektów, które można utworzyć za pomocą wywołania Tuples.pair(object1, object2). eclipse.org/collections/javadoc/9.2.0/org/eclipse/collections/…
Donald Raab
2
To chyba najbardziej prawdziwa odpowiedź na język Java. WSZYSTKIE dostępne implementacje. Bardzo dokładne, wielkie dzięki!
Mike,
Cześć @Hans Używam listy List<Pair<Integer, String> listOfTuple. Jak korzystać listOfTuple.add()? Jeśli zrobię - listOfTuple.add(Pair<someInteger, someString>);to nie zadziała. Z góry dziękuję.
Me_developer
wersja Androida nie jest dostępna w teście jednostkowym, co sprawia, że ​​tak naprawdę nie jest przydatna ...
OznOg
31

Apache common lang3 ma klasę Pair i kilka innych bibliotek wymienionych w tym wątku. Jaka jest odpowiednik pary C ++ <L, R> w Javie?

Przykład spełniający wymagania z pierwotnego pytania:

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());
}
zmienione
źródło
1
Jest to najprostsza opcja, jeśli dostępny jest Apache Commons.
Kip
18

Każdemu, kto tworzy dla Androida, możesz użyć android.util.Pair . :)

XåpplI'-I0llwlg'I -
źródło
15

Co z Pairklasą „Apache Commons Lang 3” i względnymi podklasami?

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

ImmutablePairjest specyficzną podklasą, która nie pozwala na modyfikację wartości w parze, ale istnieją inne implementacje z różnymi semantycznymi. Są to współrzędne Maven, jeśli ich potrzebujesz.

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
danidemi
źródło
11

Możesz napisać ogólną klasę Pary <A, B> i użyć jej w tablicy lub na liście. Tak, musisz napisać klasę, ale możesz ponownie użyć tej samej klasy dla wszystkich typów, więc musisz to zrobić tylko raz.

Dan Dyer
źródło
Chciałbym zobaczyć przykład tego!
DivideByHero
1
Dan, złą rzeczą jest to, że nie można zaakceptować np. Tylko Parowania <Ciągu, Liczb całkowitych> z powodu skasowania typu, nie? Ale tak samo jest z Javą ...
Johannes Weiss,
Nie sądzę, że to zadziała dla zwykłych tablic, ale na pewno zadziała dla innych kolekcji.
Outlaw Programmer
@Outlaw Programmer: Dobrze, możesz używać go z tablicami, ale jest brzydki, ponieważ musisz użyć hacka, aby stworzyć ogólne tablice.
Dan Dyer
7

Rozwijając inne odpowiedzi, ogólna niezmienna para powinna mieć metodę statyczną, aby uniknąć zaśmiecania kodu wywołaniem konstruktora:

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

jeśli nazwiesz metodę statyczną „of” lub „pairOf”, kod stanie się płynny, ponieważ możesz pisać:

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

szkoda, że ​​podstawowe biblioteki java są tak rzadkie w takich rzeczach, że do robienia takich podstawowych czynności trzeba używać commons-lang lub innych stron trzecich. kolejny powód do uaktualnienia do Scala ...

simbo1905
źródło
6

Chciałem zapytać, czy nie chcesz po prostu użyć List<Pair<T, U>>? ale oczywiście JDK nie ma klasy Pair <>. Ale szybki Google znalazł taki na Wikipedii i na forums.sun.com . Twoje zdrowie

JMD
źródło
6

Preferowanym rozwiązaniem, tak jak to opisałeś, jest Lista par (tj. Lista).

Aby to osiągnąć, należy utworzyć klasę pary do wykorzystania w swojej kolekcji. Jest to przydatna klasa narzędziowa do dodania do bazy kodu.

Najbliższą klasą w Sun JDK zapewniającą funkcjonalność podobną do typowej klasy Pair jest AbstractMap.SimpleEntry. Możesz użyć tej klasy zamiast tworzyć własną klasę Pair, choć musiałbyś żyć z niezręcznymi ograniczeniami i myślę, że większość ludzi skrzywiłaby się z tego powodu, ponieważ tak naprawdę nie jest to zamierzona rola SimpleEntry. Na przykład SimpleEntry nie ma metody „setKey ()” i domyślnego konstruktora, więc może się okazać, że jest zbyt ograniczająca.

Pamiętaj, że Kolekcje są zaprojektowane tak, aby zawierały elementy jednego typu. Powiązane interfejsy narzędzi, takie jak Mapa, nie są w rzeczywistości kolekcjami (tzn. Mapa nie implementuje interfejsu kolekcji). Para również nie implementuje interfejsu Collection, ale jest oczywiście użyteczną klasą do budowania większych struktur danych.

Jeremy Rishel
źródło
Uważam to rozwiązanie za znacznie lepsze niż „zwycięskie” z ogólną parą <K, V>. Robi wszystko, o co prosi i wychodzi z pudełka. Używam tego do mojej realizacji.
Sauer
5

Jest to oparte na kodzie JavaHelp4u.

Mniej gadatliwy i pokazuje, jak zrobić w jednym wierszu i jak zapętlać rzeczy.

//======>  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());
}
Lew Ufimcew
źródło
4

po prostu stwórz klasę jak

class tuples 
{ 
int x;
int y;
} 

następnie utwórz listę tych obiektów krotek

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

dzięki czemu możesz również implementować inne nowe struktury danych w ten sam sposób.

użytkownik93
źródło
4

Chodzi mi o to, że chociaż nie ma Pairklasy w Javie, jest coś podobnego:Map.Entry

Dokumentacja Map.Entry

To jest (trochę upraszczając) co HashMaplub właściwie dowolne Mapsklepy.

Możesz utworzyć instancję Mapprzechowującą w niej swoje wartości i uzyskać zestaw wpisów. Skończysz z tym, Set<Map.Entry<K,V>>czego skutecznie chcesz.

Więc:

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
}
SomeDude
źródło
Ta opcja ma problem z unikalnymi kluczami. Powiedzmy, że masz atrybuty osób (np. {(Person1, „niebieskooki”), (person1, „rudowłosy”, (person2, „krótkowzroczny”), (person2, „szybki myśliciel”)}) nie być w stanie przechowywać je w parach na mapie, ponieważ każda osoba jest kluczem do mapy i pozwoliłby tylko jeden atrybut na osobę.
manuelvigarcia
2

Spring ma Pair<S,T>typ w pakiecie Data Utilsorg.springframework.data.util

Pair<String,Integer> pair = Pair.of("Test", 123);
System.out.println(pair.getFirst());
System.out.println(pair.getSecond());
baranina
źródło
0

Co z com.sun.tools.javac.util.Pair?

Rene H.
źródło
7
Ten typ znajduje się w tools.jar - części implementacji kompilatora javac w „Sun”. Jest rozpowszechniany tylko jako część JDK, może nie być obecny w implementacjach innych dostawców i jest mało prawdopodobne, aby był częścią publicznego API.
McDowell,
0

Pierwszą rzeczą, o której myślę, gdy mówię o parach klucz / wartość, jest klasa właściwości, w której można zapisywać i ładować elementy do strumienia / pliku.

muka90
źródło
0

W projekcie Reactor (io.projectreactor: reactor-core) istnieje zaawansowana obsługa n-Tuples:

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

Tam możesz dostać t.getT1(), t.getT2(), ...Zwłaszcza za pomocą Stream lub Flux możesz nawet zmapować elementy krotki:

Stream<Tuple2<String, Integer>> s;
s.map(t -> t.mapT2(i -> i + 2));
Opony
źródło