Jaki jest odpowiednik pary C ++ <L, R> w Javie?


671

Czy istnieje dobry powód, dla którego nie ma Pair<L,R>Java? Jaki byłby odpowiednik tego konstruktu C ++? Wolałbym raczej nie wdrożyć własnego.

Wydaje się, że 1.6 zapewnia coś podobnego ( AbstractMap.SimpleEntry<K,V>), ale wygląda to na dość skomplikowane.


7
Dlaczego jest AbstractMap.SimpleEntryzawiłe?
CurtainDog

27
Z powodu nazwy, dowolne nazewnictwo jednego klucza i jednej wartości.
Enerccio


2
@sffc JavaFX nie znajduje się na żadnej z domyślnych ścieżek klas w JDK7, użycie go wymaga ręcznego dodania bibliotek środowiska wykonawczego JFX.
Cord Rehn

3
@Enerccio: Tak więc faktycznie stwierdzasz, że „pierwszy” i „drugi” nie jest arbitralny, podczas gdy „klucz” i „wartość” - jest? To jest jeden dobry powód, dla którego nie ma takiej klasy w zestawie SDK. Byłby wieczny spór o „właściwe” nazywanie.
fdreger

Odpowiedzi:


400

W wątkucomp.lang.java.help Hunter Gratzner podaje kilka argumentów przeciwko obecności Pairkonstruktu w Javie. Głównym argumentem jest to, że klasaPair nie przekazuje żadnej semantyki na temat relacji między tymi dwiema wartościami (skąd wiesz, co oznaczają „pierwsze” i „drugie”?).

Lepszą praktyką jest napisanie bardzo prostej klasy, takiej jak ta zaproponowana przez Mike'a, dla każdej aplikacji, którą zrobiłbyś z tej Pairklasy. Map.Entryjest przykładem pary, która ma swoje znaczenie w nazwie.

Podsumowując, moim zdaniem lepiej jest mieć klasę Position(x,y), klasę Range(begin,end)i klasę Entry(key,value)niż rodzajową Pair(first,second), która nie mówi mi nic o tym, co powinna zrobić.


143
Gratzner rozdziela włosy. Z przyjemnością zwracamy jedną wartość jako prymitywną lub wbudowaną klasę bez hermetyzacji jej w klasie. Gdybyśmy zwrócili krotkę kilkunastu elementów, nikt by się nie zgodził, że powinna mieć własną klasę. Gdzieś pośrodku znajduje się (niewyraźna) linia podziału. Myślę, że nasze mózgi jaszczurek mogą łatwo poradzić sobie z Parami.
Ian

25
Zgadzam się z Ianem. Java pozwala zwrócić int; nie zmusza cię do utworzenia aliasu dla int za każdym razem, gdy go używasz. Pary nie są bardzo różne.
Clément,

5
Gdybyśmy mogli rozpakować parę bezpośrednio do zmiennych lokalnych lub przekazać ją do metody, która przyjmuje dwa argumenty, para byłaby przydatną klasą. Ponieważ nie możemy tego tak rozpakować, tworzenie znaczącej klasy i utrzymywanie wartości razem nie wygląda tak źle. A jeśli naprawdę chcesz parę Pomimo ograniczeń, zawsze Object [2] + odlewy :-)
Marcus

Chodzi o to, że jeśli nie zgadzasz się z Gratzner, to w kilku miejscach są implementacje Pair. Zarówno Apache Commons, jak i Guava mają IIRC. Użyj tych. Ale umieszczenie czegoś w głównych bibliotekach Java oznacza, że ​​jest to szlachetny i zatwierdzony sposób robienia rzeczy (z dużymi literami), a ponieważ ludzie nie zgadzają się na to, nie powinniśmy go tam umieszczać. W starych bibliotekach jest dość cruft, nie trzeba niepotrzebnie umieszczać tam więcej.
Haakon Løtveit

1
@Dragas Kiedy potrzebuję pary wartości, to nie jest Java ... poważnie?
idclev 463035818

156

To jest Java. Musisz stworzyć własną, dopasowaną klasę Pair z opisowymi nazwami klas i pól, i nie wspominając o tym, że wymyślisz koło ponownie, pisząc hashCode () / equals () lub wdrażając Porównywalny raz za razem.


61
To nie odpowiada na pytanie „dlaczego”. (Chyba że uważasz, że odpowiedź brzmi „to jest java”)
Nikita Rybak,

127
+1 za kpiny z gadatliwości Javy. -1 za brak odpowiedzi na pytanie.
Bennett McElwee

19
Kpina z Java byłaby w porządku, gdybyś wskazał Apache Commong Lang, który zawiera klasę Pair.
haylem

6
Lub możesz po prostu użyćSimpleImmutableEntry
CurtainDog

33
Pierwsze zdanie CZY odpowiada na pytanie „dlaczego?”. To jest Java i to wszystko.
masterziv

103

Klasa par zgodna z 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
Prawdopodobnie chcesz usunąć setery i zrobić pierwszy i drugi finał, dzięki czemu para będzie niezmienna. (Jeśli ktoś zmienił komponenty po użyciu ich jako klucza skrótu, zdarzają się dziwne rzeczy).
Thilo

21
return „(” + first.toString () + ”,„ + second.toString () + ”)” w metodzie toString () może zgłaszać wyjątki NullPointerExceptions. To jest lepsze: return „(„ + first + ”,„ + second + ”)”;
Juha Syrjälä

6
Zaznacz też parę jako „końcową” lub zmień pierwszą linię równości na „if (other! = Null && this.getClass () == other.getClass ())”
sargas

8
Przepraszam za losowe pytanie nooby, ale dlaczego masz wywołanie super () w konstruktorze?
Ibrahim,

6
@Ibrahim: W tym przypadku jest to zbędne - zachowanie jest dokładnie takie samo, jeśli wyjmiesz super(). Normalnie odciąłbym go, jeśli jest opcjonalny, tak jak jest tutaj.
Chris Jester-Young

53

Najkrótsza para, jaką mogłam wymyślić, jest następująca, używając Lombok :

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

Posiada wszystkie zalety odpowiedzi z @arturh (z wyjątkiem porównywalności), ma hashCode, equals, toStringa statyczne „konstruktor”.


Sprytne! Lubiłem to!
Ahmet Ipkin


31

Kolejny sposób na wdrożenie Parowania z.

  • Publiczne niezmienne pola, tj. Prosta struktura danych.
  • Porównywalny.
  • Prosty skrót i jest równy.
  • Prosta fabryka, więc nie musisz podawać typów. np. Pair.of („cześć”, 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
Lubię statyczną metodę fabryczną of. Przypomina niezmienne kolekcje Google Guava .
Jarek Przygódzki

7
Jesteś w pewnym momencie rzucania o1się Comparable, choć nic nie wskazuje na to rzeczywiście wdrożyć ten interfejs. Jeśli jest to wymóg, FIRSTparametr typu powinien mieć wartość FIRST extends Comparable<?>.
G_H

Nie jestem facetem z javy, więc proszę wybacz mi moją ignorancję, ale o jakich klasach pomocników myślisz w komentarzach do zrobienia TODO?

3
31 jest złą stałą dla hashCode. Na przykład, jeśli użyjesz HashMap z kluczem Pair <Integer, Integer> dla mapy 2D, otrzymasz wiele kolizji. Na przykład lepiej byłoby (a * 65497) ^ b.
Michał Zieliński

1
@MarioCarneiro ^ to Xor, a nie potęga
Michał Zieliński

27

Co powiesz na http://www.javatuples.org/index.html Uważam, że jest to bardzo przydatne.

Javatuples oferuje tuple klas od jednego do dziesięciu elementów:

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
Zabawne, ale jest co najmniej 5 klas więcej, niż mogłem sobie wyobrazić.
maaartinus,

3
@maaartinus Co najmniej 10 więcej niż bym użył.
Boann

7
@ Boann: OK, jestem poprawiony. Kiedyś używałem Pairi mogłem sobie wyobrazić, że mogę używać Tripletraz na 50 lat. Teraz używam Lombok i tworzę małą 4-liniową klasę za każdym razem, gdy potrzebuję pary. Zatem „10 za dużo” jest dokładne.
maaartinus

5
Czy potrzebujemy Bottom (0 element)klasy? :)
Earth Engine

2
Wow, to jest brzydkie. Wiem, że starają się to wyrazić, ale Tuple z przeciążonymi parametrami jak w C # byłoby ładniejsze.
arviman

12

To zależy od tego, do czego chcesz go użyć. Typowym powodem jest iteracja po mapach, dla których po prostu to robisz (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
Nie jestem pewien, czy klasa niestandardowa pomogłaby w tym przypadku :)
Nikita Rybak,

31
„Typowym powodem jest iteracja po mapach”. Naprawdę?
Bennett McElwee

12

Android zapewnia Pairklasę ( http://developer.android.com/reference/android/util/Pair.html ), tutaj implementacja:

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(..)wymaga biblioteki Guava.
Markus L

3
Zmień go na Objects.equals(...)język Java w 2011 r. (1.7).
AndrewF

9

Największym problemem jest prawdopodobnie to, że nie można zapewnić niezmienności na A i B (zobacz Jak zapewnić, aby parametry typu były niezmienne ), więc hashCode()może dawać niespójne wyniki dla tej samej Pary po wstawieniu do kolekcji (na przykład dałoby to nieokreślone zachowanie , patrz Definiowanie równych pod względem zmiennych pól ). Dla konkretnej (nie ogólnej) klasy par programista może zapewnić niezmienność, ostrożnie wybierając A i B jako niezmienne.

W każdym razie, usunięcie ostrzeżeń generycznych z odpowiedzi @ 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 + ')';
    }
}

Dodatki / poprawki bardzo mile widziane :) W szczególności nie jestem pewien co do mojego wykorzystania Pair<?, ?> .

Aby uzyskać więcej informacji o tym, dlaczego ta składnia, zobacz Zapewnienie implementacji obiektów Porównywalne oraz szczegółowe wyjaśnienie Jak zaimplementować ogólną max(Comparable a, Comparable b)funkcję w Javie?


Ponieważ liczby całkowite Java są 32-bitowe, czy pomnożenie pierwszego kodu mieszającego przez 31 nie oznacza, że ​​się przepełnia? Czy nie byłoby lepiej wykonać ekskluzywne OR?
Dan

@Dan możesz edytować Edytuj Przeprowadziłem się z java :)
Mr_and_Mrs_D

5

Moim zdaniem nie ma pary w Javie, ponieważ jeśli chcesz dodać dodatkową funkcjonalność bezpośrednio na parze (np. Porównywalne), musisz powiązać typy. W C ++ po prostu nas to nie obchodzi, a jeśli typy tworzące parę nie mają operator <, topair::operator < nie będą się również kompilować.

Przykład porównywalnego bez ograniczenia:

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

Przykład Porównywalny z kontrolą czasu kompilacji, czy argumenty typu są porównywalne:

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

To dobrze, ale tym razem nie możesz używać nieporównywalnych typów jako argumentów typu w Pair. Można użyć wielu Komparatorów dla Pary w niektórych klasach użytkowych, ale ludzie w C ++ mogą tego nie dostać. Innym sposobem jest napisanie wielu klas w hierarchii typów z różnymi granicami argumentów typu, ale istnieje zbyt wiele możliwych granic i ich kombinacji ...


5

JavaFX (dostarczany w pakiecie z Javą 8) ma klasę Pair <A, B>


1
Realizacja hashcode w javafx.util.Pair może prowadzić do kolizji w błahych sprawach. Używanie go w HashMap / HashTable będzie nadal działać, ponieważ Java sprawdza równość wartości oprócz kodów skrótu, ale należy o tym pamiętać.
sffc

Jest to bardzo standardowa i często zalecana implementacja hashCode. Każdy wywoływany kod powinien oczekiwać kolizji hashCode(). Zauważ, że sama Java nie wywołuje tej metody. Dotyczy kodu użytkownika, w tym bibliotek.
AndrewF

5

Jak wielu innych już powiedziało, tak naprawdę zależy od przypadku użycia, czy klasa Pair jest przydatna, czy nie.

Myślę, że dla funkcji prywatnego pomocnika jest całkowicie uzasadnione użycie klasy Pair, jeśli dzięki temu kod jest bardziej czytelny i nie jest warte wysiłku, aby stworzyć kolejną klasę wartości z całym kodem płyty kotłowej.

Z drugiej strony, jeśli Twój poziom abstrakcji wymaga wyraźnego udokumentowania semantyki klasy zawierającej dwa obiekty lub wartości, powinieneś napisać dla niej klasę. Zwykle dzieje się tak, jeśli dane są obiektem biznesowym.

Jak zawsze wymaga umiejętnego osądu.

Na drugie pytanie polecam klasę Pair z bibliotek Apache Commons. Można je uznać za rozszerzone standardowe biblioteki dla Java:

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

Warto również zapoznać się z EqualsBuilder , HashCodeBuilder i ToStringBuilder Apache Commons , które upraszczają zapisywanie klas wartości dla obiektów biznesowych.


Zaktualizowany adres URL to commons.apache.org/lang/api-release/index.html?org/apache/…, ponieważ commons-lang3 nie ma wersji beta. Jest to nawet krótsze niż moje własne rozwiązanie Lombok, jeśli już używasz commons-lang 3.
Michael Piefel


5

Dobra Nowina JavaFXma kluczową wartość Para.

wystarczy dodać javafx jako zależność i zaimportować javafx.util.Pair ;

i użyj po prostu jak w c++.

Pair <Key, Value> 

na przykład

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

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

Zła wiadomość jest taka, że ​​nie wszyscy używają JavaFX
Michał Dobi Dobrzański

4

Interfejs Map.Entry jest bardzo zbliżony do pary c ++. Spójrz na konkretne wdrożenie, takie jak AbstractMap.SimpleEntry i AbstractMap.SimpleImmutableEntry Pierwszy element to getKey (), a drugi to getValue ().


1
OP już wie o tej opcji i zostało to szczegółowo omówione.
Keegan


3

Zgodnie z naturą języka Java, przypuszczam, że ludzie tak naprawdę nie potrzebują Pairinterfejsu, którego zwykle potrzebują. Oto przykład:

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

Gdy więc ludzie chcą zwrócić dwie wartości, mogą wykonać następujące czynności:

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

Jest to dość lekkie rozwiązanie, które odpowiada na pytanie „Co to jest semantyczne Pair<L,R>?”. Odpowiedź brzmi: jest to kompilacja interfejsu z dwoma (może być różnymi) typami i ma metody zwracania każdego z nich. Od ciebie zależy, czy dodasz do niego kolejne semantyczne. Na przykład, jeśli używasz pozycji i NAPRAWDĘ chcesz wskazać to w swoim kodzie, możesz zdefiniować PositionXi PositionYto Integer, aby utworzyć Pair<PositionX,PositionY>. Jeśli JSR 308 jest dostępny, możesz również użyć go w Pair<@PositionX Integer, @PositionY Ingeger>celu uproszczenia.

EDYCJA: Jedną rzeczą, którą powinienem tutaj wskazać, jest to, że powyższa definicja wyraźnie odnosi się do nazwy parametru typu i nazwy metody. To odpowiedź na te argumenty, że Pairbrak jest informacji semantycznej. W rzeczywistości metoda ta getLoznacza „daj mi element odpowiadający typowi parametru typu L”, co coś znaczy.

EDYCJA: Oto prosta klasa narzędzi, która może ułatwić życie:

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

stosowanie:

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

Co o equals, hashCodei toString?
sdgfsdh

to tylko minimalna implementacja. Jeśli potrzebujesz więcej, możesz napisać kilka funkcji pomocniczych, aby to ułatwić, ale nadal musisz napisać kod.
Earth Engine

Aby wdrożyć toString, potrzebujesz więcej wiedzy na temat relacji między tymi dwoma polami.
Earth Engine

Chodzi mi o to, że classmoże być lepsze niż tylko interfacedlatego, że może zaimplementować te rzeczy.
sdgfsdh

3

Pomimo podobnej składni, Java i C ++ mają bardzo różne paradygmaty. Pisanie C ++ jak Java jest złym C ++, a pisanie Javy jak C ++ jest złe Java.

Dzięki IDE opartemu na odbiciu, takim jak Eclipse, pisanie niezbędnej funkcjonalności klasy „parowej” jest szybkie i proste. Utwórz klasę, zdefiniuj dwa pola, użyj różnych opcji menu „Generuj XX”, aby wypełnić klasę w ciągu kilku sekund. Być może będziesz musiał bardzo szybko wpisać „porównaj”, jeśli chcesz interfejs porównywalny.

Dzięki osobnym opcjom deklaracji / definicji w języku generatory kodu C ++ nie są tak dobre, więc ręczne pisanie małych klas narzędzi jest bardziej czasochłonne. Ponieważ para jest szablonem, nie musisz płacić za funkcje, których nie używasz, a funkcja typedef umożliwia przypisywanie znaczących nazw typów do kodu, więc zastrzeżenia dotyczące „braku semantyki” tak naprawdę nie wytrzymują.


2

Para byłaby dobrą rzeczą, aby być podstawową jednostką konstrukcyjną dla złożonych generycznych, na przykład, to jest z mojego kodu:

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

To jest tak samo jak Tuple Haskella


1
Teraz mogę powiedzieć, że użycie pary <A, B> sprawia, że ​​kod jest mniej pouczający, a implementacja specjalnych obiektów zamiast użycia pary jest znacznie lepsza
Illarion Kovalchuk

1
Lepsze lub gorsze. Wyobraź sobie, że masz funkcję łączącą dwa argumenty (np. Łączącą wykresy w jeden) i musisz ją buforować. Tutaj Pairjest optymalny, ponieważ nie ma specjalnej semantyki. Posiadanie jasnej nazwy dla jasnej koncepcji jest dobre, ale szukanie nazwy, w której „pierwsze” i „drugie” działają dobrze, nie jest.
maaartinus

2

Prosty sposób Object [] - może być użyty jako krotka wymiarowa


2
Dowolny wymiar, tak. Ale: uciążliwe w tworzeniu i nie bezpieczne dla typu.
Michael Piefel,

2

W przypadku języków programowania, takich jak Java, alternatywna struktura danych używana przez większość programistów do reprezentowania pary, podobnie jak struktury danych, to dwie tablice, a dane są dostępne za pośrednictwem tego samego indeksu

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

Nie jest to idealne, ponieważ dane powinny być ze sobą powiązane, ale okazują się również dość tanie. Ponadto, jeśli twój przypadek użycia wymaga przechowywania współrzędnych, lepiej zbudować własną strukturę danych.

Mam coś takiego w swojej bibliotece

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


2

Oto niektóre biblioteki, które mają wiele stopni krotek dla Twojej wygody:

  • JavaTuples . Wszystko, co ma w kratkach od stopnia 1-10.
  • JavaSlang . Krotki od stopnia 0-8 i wiele innych dodatków funkcjonalnych.
  • jOOλ . Krotki z stopnia 0-16 i inne funkcjonalne gadżety. (Oświadczenie, pracuję dla firmy zarządzającej)
  • Funkcjonalna Java . Krotki od stopnia 0-8 i wiele innych dodatków funkcjonalnych.

Wspomniano o innych bibliotekach zawierających przynajmniej Pairkrotkę.

W szczególności, w kontekście programowania funkcjonalnego, w którym wykorzystuje się dużo typowania strukturalnego, a nie typowego ( jak zaleca się w przyjętej odpowiedzi ), te biblioteki i ich krotki są bardzo przydatne.



2

kolejna implementacja Terse Lombok

import lombok.Value;

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

1

Zauważyłem, że wszystkie implementacje Pary są tu rozrzucone, przypisując znaczenie kolejności dwóch wartości. Kiedy myślę o parze, myślę o kombinacji dwóch elementów, w których kolejność tych dwóch elementów nie ma znaczenia. Oto moja implementacja nieuporządkowanej pary, z hashCodei equalszastępuje, aby zapewnić pożądane zachowanie w kolekcjach. Można także klonować.

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

    }

}

Ta implementacja została poprawnie przetestowana jednostkowo i wypróbowano użycie w zestawie i mapie.

Zauważ, że nie twierdzę, że opublikuję to w domenie publicznej. To jest kod, który właśnie napisałem do użytku w aplikacji, więc jeśli zamierzasz go użyć, powstrzymaj się od robienia bezpośredniej kopii i popsuć trochę komentarzami i nazwiskami. Złapać mój dryf?


3
w rzeczywistości sprawdź na dole każdej strony: „wkład użytkowników na licencji cc-wiki”
amara

Ach, nie zauważyłem tego. Dzięki za zgłoszenie się. W takim przypadku użyj kodu zgodnego z licencją.
G_H

1
Pytanie dotyczy pary równoważnej w C ++ - która jest uporządkowana. Myślę też, że dopóki istnieje odniesienie do obiektu Pary, a te są zmienne, wstawianie Par w kolekcjach może prowadzić do nieokreślonego zachowania.
Mr_and_Mrs_D


1

com.sun.tools.javac.util.Pair to prosta implementacja pary. Można go znaleźć w pliku jdk1.7.0_51 \ lib \ tools.jar.

Poza org.apache.commons.lang3.tuple.Pair, nie jest to tylko interfejs.


2
Nikt jednak nie powinien używać wewnętrznych interfejsów API JDK.
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;
    }

}

stosowanie :

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

Niezmienne, tylko para!


Och, wow. Inny? Spróbuj użyć swoich z bardziej złożonymi rodzajami - w pewnym momencie nie będzie można wnioskować o odpowiednich typach. Dodatkowo, powinno być możliwe: Pair<Object, Object> pair = Pair.createPair("abc", "def")ale myślę, że trzeba napisać za Pair.createPair((Object)"abc", (Object)"def")pomocą twojego kodu?
Ma ZAKOŃCZENIE - Anony-Mousse,

możesz zastąpić metodę statyczną: @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); } ale nie wiem, czy to dobra praktyka
Bastiflew,

Nie, to prawdopodobnie jeszcze bardziej spieprzy sprawę. Z mojego doświadczenia wynika, że ​​przynajmniej jeden z kompilatorów (wypróbuj java6, java7, javadoc i eclipse java) będzie narzekał. Tradycyjny new Pair<Object, Object>("abc", "def")był najbardziej niezawodny w moich eksperymentach.
Ma ZAKOŃCZENIE - Anony-Mousse,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.