Was entspricht dem C ++ - Paar <L, R> in Java?

670

Gibt es einen guten Grund, warum es keinen gibt? Pair<L,R> in Java ? Was wäre das Äquivalent zu diesem C ++ - Konstrukt? Ich würde es lieber vermeiden, meine eigenen neu zu implementieren.

Es scheint, dass 1.6 etwas Ähnliches liefert ( AbstractMap.SimpleEntry<K,V>), aber das sieht ziemlich kompliziert aus.

David Segonds
quelle
7
Warum ist AbstractMap.SimpleEntryverschlungen?
CurtainDog
27
Aufgrund der Benennung willkürliche Benennung eines Schlüssels und eines Werts.
Enerccio
2
@sffc JavaFX befindet sich in keinem der Standardklassenpfade in JDK7. Für die Verwendung müssen die JFX-Laufzeitbibliotheken manuell hinzugefügt werden.
Cord Rehn
3
@Enerccio: Sie geben also tatsächlich an, dass "first" und "second" nicht willkürlich sind, während "key" und "value" - sind? Dann ist dies ein guter Grund, eine solche Klasse nicht im SDK zu haben. Es würde einen ewigen Streit um die "richtige" Benennung geben.
fdreger

Antworten:

400

In einem Thread übercomp.lang.java.help gibt Hunter Gratzner einige Argumente gegen das Vorhandensein eines PairKonstrukts in Java. Das Hauptargument ist, dass eine KlassePair keine Semantik über die Beziehung zwischen den beiden Werten vermittelt (woher wissen Sie, was "erste" und "zweite" bedeuten?).

Eine bessere Vorgehensweise besteht darin, für jede Anwendung, die Sie für die PairKlasse gemacht hätten, eine sehr einfache Klasse zu schreiben, wie die von Mike vorgeschlagene . Map.Entryist ein Beispiel für ein Paar, das seine Bedeutung im Namen trägt.

Zusammenfassend ist es meiner Meinung nach besser, eine Klasse Position(x,y), eine Klasse Range(begin,end)und eine Klasse zu haben , Entry(key,value)als ein Generikum Pair(first,second), das mir nichts darüber sagt, was es tun soll.

Luc Touraille
quelle
143
Gratzner spaltet Haare. Wir freuen uns sehr, einen einzelnen Wert als primitive oder integrierte Klasse zurückzugeben, ohne ihn in einer Klasse zu kapseln. Wenn wir ein Tupel von einem Dutzend Elementen zurückgeben würden, würde niemand anderer Meinung sein, dass es eine eigene Klasse haben sollte. Irgendwo in der Mitte befindet sich eine (unscharfe) Trennlinie. Ich denke, unser Eidechsengehirn kann mit Paaren leicht genug umgehen.
Ian
25
Ich stimme Ian zu. Mit Java können Sie int zurückgeben. Sie müssen nicht jedes Mal einen Alias ​​für int erstellen, wenn Sie einen verwenden. Paare sind nicht sehr unterschiedlich.
Clément
5
Wenn wir ein Paar direkt in Ihre lokalen Variablen entpacken oder an eine Methode weiterleiten könnten, die zwei Argumente akzeptiert, wäre Pair eine nützliche Klasse. Da wir es nicht so auspacken können, sieht es nicht schlecht aus, eine aussagekräftige Klasse zu erstellen und die Werte zusammenzuhalten. Und wenn Sie trotz der Einschränkungen wirklich ein Paar wollen, gibt es immer Object [2] + Casts :-)
Marcus
Die Sache ist, dass wenn Sie mit Gratzner nicht einverstanden sind, es an mehreren Stellen Paarimplementierungen gibt. Apache Commons und Guava haben beide IIRC. Verwenden Sie diese. Aber etwas in die Hauptbibliotheken von Java aufzunehmen, bedeutet, dass es eine edle und anerkannte Art ist, Dinge zu tun (mit Großschreibung), und da die Leute sich nicht einig sind, sollten wir es nicht dort ablegen. Es gibt genug Cruft in den alten Bibliotheken, wie es ist, lassen Sie uns nicht unnötig mehr dort setzen.
Haakon Løtveit
1
@Dragas Wenn ich ein Wertepaar brauche, dann ist das nicht Java ... im Ernst?
idclev 463035818
156

Das ist Java. Sie müssen Ihre eigene maßgeschneiderte Pair-Klasse mit beschreibenden Klassen- und Feldnamen erstellen und müssen nicht vergessen, dass Sie das Rad neu erfinden, indem Sie hashCode () / equals () schreiben oder Comparable immer wieder implementieren.

Andreas Krey
quelle
61
Das beantwortet nicht die Frage "warum". (Es sei denn, Sie halten "dies ist Java" für eine Antwort)
Nikita Rybak
127
+1 für das Verspotten von Javas Ausführlichkeit. -1 für die Nichtbeantwortung der Frage.
Bennett McElwee
19
Der Java-Spott wäre in Ordnung gewesen, wenn Sie auf die Apache Commong Lang hingewiesen hätten, die eine Pair-Klasse enthält.
Haylem
6
Oder Sie könnten einfach verwendenSimpleImmutableEntry
CurtainDog
33
Der erste Satz beantwortet die Frage "warum?". Das ist Java und das wars.
Masterziv
103

HashMap-kompatible Paarklasse:

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;
    }
}
arturh
quelle
136
Sie möchten wahrscheinlich die Setter löschen und das erste und zweite Finale erreichen, wodurch das Paar unveränderlich wird. (Wenn jemand die Komponenten geändert hat, nachdem er sie als Hash-Schlüssel verwendet hat, passieren seltsame Dinge).
Thilo
21
Rückgabe "(" + first.toString () + "," + second.toString () + ")" in der toString () -Methode kann NullPointerExceptions auslösen. Dies ist besser: return "(" + first + "," + second + ")";
Juha Syrjälä
6
Markieren Sie das Paar entweder als "final" oder ändern Sie die erste Zeile gleich "if (other! = Null && this.getClass () == other.getClass ())"
sargas
8
Entschuldigen Sie die zufällige Nooby-Frage, aber warum haben Sie im Konstruktor einen Aufruf von super ()?
Ibrahim
6
@Ibrahim: In diesem Fall ist es überflüssig - das Verhalten ist genau das gleiche, wenn Sie das super()herausgenommen haben. Normalerweise würde ich es einfach abbrechen, wenn es optional ist, so wie es hier ist.
Chris Jester-Young
53

Das kürzeste Paar, das ich mit Lombok finden konnte, ist das folgende :

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

Es verfügt über alle Vorteile der Antwort von @arturh ( mit Ausnahme der Vergleichbarkeit), hat sie hashCode, equals, toStringund eine statische „Konstruktor“.

Michael Piefel
quelle
Raffiniert! Mochte es!
Ahmet Ipkin
31

Eine andere Möglichkeit, Pair mit zu implementieren.

  • Öffentliche unveränderliche Felder, dh einfache Datenstruktur.
  • Vergleichbar.
  • Einfacher Hash und gleich.
  • Einfache Fabrik, so dass Sie die Typen nicht angeben müssen. zB Pair.of ("Hallo", 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 + ')';
        }
    }
Peter Lawrey
quelle
10
Ich mag statische Fabrikmethode of. Es erinnert an unveränderliche Sammlungen von Google Guava .
Jarek Przygódzki
7
Sie sind an einem gewissen Punkt Gießen o1zu Comparable, obwohl nichts gibt es tatsächlich , dass die Schnittstelle implementieren. Wenn dies erforderlich ist, sollte der FIRSTTypparameter sein FIRST extends Comparable<?>.
G_H
Ich bin kein Java-Typ, bitte verzeihen Sie mir meine Unwissenheit, aber an welche Art von Hilfsklassen haben Sie in den TODO-Kommentaren gedacht?
3
31 ist eine schlechte Konstante für hashCode. Wenn Sie beispielsweise HashMap verwenden, das mit Pair <Integer, Integer> für eine 2D-Karte eingegeben wurde, erhalten Sie viele Kollisionen. Zum Beispiel (a * 65497) ^ b wäre besser geeignet.
Michał Zieliński
1
@MarioCarneiro ^ ist xor, nicht Macht
Michał Zieliński
27

Wie wäre es mit http://www.javatuples.org/index.html Ich habe es sehr nützlich gefunden.

Das Javatuples bietet Ihnen Tupelklassen mit einem bis zehn Elementen:

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)
Cyberoblivion
quelle
6
Witzig, aber es gibt mindestens 5 Klassen mehr, als ich mir jemals vorstellen könnte.
Maaartinus
3
@maaartinus Mindestens 10 mehr als ich verwenden würde.
Boann
7
@ Boann: OK, ich bleibe korrigiert. Früher habe ich es benutzt Pairund konnte mir vorstellen, es Tripletvielleicht alle 50 Jahre einmal zu benutzen . Jetzt benutze ich Lombok und erstelle jedes Mal, wenn ich ein Paar brauche, eine winzige 4-Zeilen-Klasse. "10 zu viel" ist also genau.
Maaartinus
5
Brauchen wir eine Bottom (0 element)Klasse? :)
Earth Engine
2
Wow das ist hässlich. Ich weiß, dass sie versuchen, es explizit zu machen, aber ein Tupel mit überladenen Parametern wie in C # wäre schöner gewesen.
Arviman
12

Es hängt davon ab, wofür Sie es verwenden möchten. Der typische Grund dafür ist das Durchlaufen von Karten, für die Sie dies einfach tun (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());
}
Cletus
quelle
1
Ich bin nicht sicher, ob eine benutzerdefinierte Klasse in diesem Fall helfen würde :)
Nikita Rybak
31
"Der typische Grund dafür ist das Durchlaufen von Karten". "Ja wirklich?"
Bennett McElwee
12

Android bietet PairKlasse ( http://developer.android.com/reference/android/util/Pair.html ), hier die Implementierung:

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);
    }
}
Sherpya
quelle
1
Objects.equal(..)benötigt die Guava-Bibliothek.
Markus L
3
Ändern Sie es, Objects.equals(...)das seit 2011 in Java ist (1.7).
AndrewF
9

Das größte Problem ist wahrscheinlich, dass man die Unveränderlichkeit auf A und B nicht sicherstellen kann (siehe So stellen Sie sicher, dass Typparameter unveränderlich sind ), so dass hashCode()beispielsweise nach dem Einfügen in eine Sammlung inkonsistente Ergebnisse für dasselbe Paar erzielt werden können (dies würde zu undefiniertem Verhalten führen , siehe Gleichheit in Bezug auf veränderbare Felder definieren ). Für eine bestimmte (nicht generische) Paarklasse kann der Programmierer die Unveränderlichkeit sicherstellen, indem er A und B sorgfältig als unveränderlich auswählt.

Wie auch immer, Löschen der Warnungen von Generika aus der Antwort von @ 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 + ')';
    }
}

Ergänzungen / Korrekturen sehr willkommen :) Insbesondere bin ich mir über meine Verwendung von nicht ganz sicher Pair<?, ?>.

Weitere Informationen dazu, warum diese Syntax verwendet wird, finden Sie unter Sicherstellen, dass Objekte Comparable implementieren. Eine ausführliche Erläuterung finden Sie unter Implementieren einer generischen max(Comparable a, Comparable b)Funktion in Java.

Mr_and_Mrs_D
quelle
Wenn Java-Ganzzahlen 32-Bit sind, bedeutet das Multiplizieren des ersten Hashcodes mit 31 nicht, dass er überläuft? Wäre es nicht besser, einen exklusiven OP durchzuführen?
Dan
@Dan zögern Sie nicht zu bearbeiten Ich bin von Java
weggezogen
5

Meiner Meinung nach gibt es in Java kein Paar, da Sie die Typen binden müssen, wenn Sie dem Paar zusätzliche Funktionen direkt hinzufügen möchten (z. B. Vergleichbar). In C ++ ist es uns einfach egal, und wenn Typen, aus denen ein Paar besteht, keine haben operator <, wird das pair::operator <auch nicht kompiliert.

Ein Beispiel für Vergleichbar ohne Begrenzung:

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

Ein Beispiel für die Prüfung von Vergleichbar mit Kompilierungszeit, ob Typargumente vergleichbar sind:

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

Dies ist gut, aber dieses Mal können Sie möglicherweise keine nicht vergleichbaren Typen als Typargumente in Pair verwenden. In einigen Utility-Klassen werden möglicherweise viele Comparators for Pair verwendet, aber C ++ - Benutzer erhalten diese möglicherweise nicht. Eine andere Möglichkeit besteht darin, viele Klassen in einer Typhierarchie mit unterschiedlichen Grenzen für Typargumente zu schreiben, aber es gibt zu viele mögliche Grenzen und deren Kombinationen ...

MaxBuzz
quelle
5

JavaFX (im Lieferumfang von Java 8 enthalten) verfügt über die Klasse Pair <A, B>

CarrKnight
quelle
1
Die Implementierung von hashCode in javafx.util.Pair kann zu Kollisionen in trivialen Fällen führen. Die Verwendung in HashMap / HashTable funktioniert weiterhin, da Java zusätzlich zu den Hash-Codes die Gleichheit der Werte überprüft, dies ist jedoch zu beachten.
sffc
Dies ist eine sehr standardmäßige und allgemein empfohlene HashCode-Implementierung. Kollisionen sollten von jedem Code erwartet werden , der aufruft hashCode(). Beachten Sie, dass Java selbst diese Methode nicht aufruft. Es ist für Benutzercode, einschließlich Bibliotheken.
AndrewF
5

Wie viele andere bereits festgestellt haben, hängt es wirklich vom Anwendungsfall ab, ob eine Pair-Klasse nützlich ist oder nicht.

Ich denke, für eine private Hilfsfunktion ist es absolut legitim, eine Pair-Klasse zu verwenden, wenn dies Ihren Code lesbarer macht und die Mühe nicht wert ist, eine weitere Wertklasse mit all ihrem Kesselplattencode zu erstellen.

Wenn Sie andererseits auf Ihrer Abstraktionsebene die Semantik der Klasse, die zwei Objekte oder Werte enthält, klar dokumentieren müssen, sollten Sie eine Klasse dafür schreiben. Normalerweise ist dies der Fall, wenn die Daten ein Geschäftsobjekt sind.

Wie immer erfordert es ein geschicktes Urteilsvermögen.

Für Ihre zweite Frage empfehle ich die Pair-Klasse aus den Apache Commons-Bibliotheken. Diese können als erweiterte Standardbibliotheken für Java betrachtet werden:

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

Vielleicht möchten Sie auch einen Blick auf EqualsBuilder , HashCodeBuilder und ToStringBuilder von Apache Commons werfen , die das Schreiben von Wertklassen für Ihre Geschäftsobjekte vereinfachen.

Peter Goetz
quelle
Die aktualisierte URL lautet commons.apache.org/lang/api-release/index.html?org/apache/…, da commons-lang3 keine Beta mehr hat. Dies ist sogar kürzer als meine eigene Lombok-Lösung, wenn Sie bereits commons-lang 3 verwenden.
Michael Piefel
5

Gute Nachrichten JavaFXhaben ein Schlüsselwertpaar.

Fügen Sie einfach Javafx als Abhängigkeit hinzu und importieren Sie es javafx.util.Pair.

und einfach wie in verwenden c++.

Pair <Key, Value> 

z.B

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

pr.get(key);// will return corresponding value
RAJAN_PARMAR
quelle
Schlechte Nachrichten sind, dass nicht jeder JavaFX verwendet
Michał Dobi Dobrzański
4

Die Map.Entry- Oberfläche kommt dem C ++ - Paar ziemlich nahe. Schauen Sie sich die konkrete Implementierung wie AbstractMap.SimpleEntry und AbstractMap.SimpleImmutableEntry an. Das erste Element ist getKey () und das zweite ist getValue ().

Nikunj Bhagat
quelle
1
OP ist diese Option bereits bekannt und wurde ausführlich erörtert.
Keegan
4
Collections.singletonMap(left, rigth);
Denis Arkharov
quelle
3

Entsprechend der Natur der Java-Sprache benötigen die PairBenutzer vermutlich keine , eine Schnittstelle ist normalerweise das, was sie benötigen. Hier ist ein Beispiel:

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

Wenn Benutzer zwei Werte zurückgeben möchten, können sie Folgendes tun:

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

Dies ist eine ziemlich leichte Lösung, die die Frage "Was ist die Semantik von a Pair<L,R>?" Beantwortet . Die Antwort lautet: Dies ist eine Schnittstellenerstellung mit zwei (möglicherweise unterschiedlichen) Typen, und es gibt Methoden, um jeden von ihnen zurückzugeben. Es liegt an Ihnen, weitere Semantik hinzuzufügen. Wenn Sie Position zum Beispiel verwenden und wirklich wollen , um es in Sie Code angeben, können Sie festlegen , PositionXund PositionYdas enthält Integer, um ein Make - up Pair<PositionX,PositionY>. Wenn JSR 308 verfügbar ist, können Sie auch verwendenPair<@PositionX Integer, @PositionY Ingeger> , um dies zu vereinfachen.

EDIT: Eine Sache, die ich hier angeben sollte, ist, dass die obige Definition den Typparameternamen und den Methodennamen explizit in Beziehung setzt. Dies ist eine Antwort auf diese Argumente, dass Paires an semantischer Information mangelt. Tatsächlich getLbedeutet die Methode "Gib mir das Element, das dem Typ des Typparameters L entspricht", was etwas bedeutet.

EDIT: Hier ist eine einfache Utility-Klasse, die das Leben leichter machen kann:

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

Verwendungszweck:

return Pairs.makePair(new Integer(100), "123");
Erdmotor
quelle
Was equalsist mit hashCodeund toString?
SDGFSDH
Nun, dies ist nur eine minimale Implementierung. Wenn Sie mehr als das benötigen, können Sie einige Hilfsfunktionen schreiben, um es einfacher zu machen, aber Sie müssen trotzdem den Code schreiben.
Earth Engine
Zur Implementierung toStringbenötigen Sie mehr Wissen über die Beziehung zwischen den beiden Feldern.
Earth Engine
Mein Punkt ist, dass ein classmöglicherweise besser ist als nur ein, interfaceweil es diese Dinge implementieren kann.
SDGFSDH
3

Obwohl Java und C ++ syntaktisch ähnlich sind, haben sie sehr unterschiedliche Paradigmen. Schreiben von C ++ wie Java ist schlechtes C ++, und Schreiben von Java wie C ++ ist schlechtes Java.

Mit einer reflexionsbasierten IDE wie Eclipse ist das Schreiben der erforderlichen Funktionalität einer "Paar" -Klasse schnell und einfach. Klasse erstellen, zwei Felder definieren, die verschiedenen Menüoptionen "XX generieren" verwenden, um die Klasse in Sekundenschnelle auszufüllen. Vielleicht müssten Sie ganz schnell ein "compareTo" eingeben, wenn Sie die vergleichbare Oberfläche möchten.

Mit separaten Deklarations- / Definitionsoptionen in der Sprache sind C ++ - Codegeneratoren nicht so gut, so dass das manuelle Schreiben kleiner Dienstprogrammklassen zeitaufwändiger ist. Da es sich bei dem Paar um eine Vorlage handelt, müssen Sie nicht für Funktionen bezahlen, die Sie nicht verwenden, und die typedef-Funktion ermöglicht das Zuweisen aussagekräftiger Typnamen zum Code, sodass die Einwände gegen "keine Semantik" nicht wirklich zutreffen.

gerardw
quelle
2

Paar wäre ein gutes Zeug, um eine grundlegende Konstruktionseinheit für ein komplexes Generikum zu sein, zum Beispiel ist dies aus meinem Code:

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

Es ist genauso wie Haskells Tupel

Illarion Kovalchuk
quelle
1
Jetzt kann ich sagen, dass die Verwendung von Pair <A, B> den Code weniger informativ macht und die Implementierung spezieller Objekte anstelle von Pair viel besser ist
Illarion Kovalchuk
1
Besser oder schlechter. Stellen Sie sich vor, Sie haben eine Funktion, die ihre beiden Argumente kombiniert (z. B. Diagramme zu einem zusammenführt) und müssen sie zwischenspeichern. Hier Pairist optimal, da es keine spezielle Semantik gibt. Einen klaren Namen für ein klares Konzept zu haben ist gut, aber nach einem Namen zu suchen, bei dem "erste" und "zweite" gut funktionieren, ist nicht gut.
Maaartinus
2

Einfacher Weg Objekt [] - kann als jedes Dimensionstupel verwendet werden

Testus
quelle
2
Jede Dimension, ja. Aber: Umständlich zu erstellen und nicht typsicher.
Michael Piefel
2

Bei Programmiersprachen wie Java besteht die alternative Datenstruktur, die von den meisten Programmierern zur Darstellung paarweiser Datenstrukturen verwendet wird, aus zwei Arrays, und auf Daten wird über denselben Index zugegriffen

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

Dies ist nicht ideal, da die Daten miteinander verknüpft werden sollten, sich aber auch als ziemlich billig herausstellen. Wenn Ihr Anwendungsfall das Speichern von Koordinaten erfordert, ist es besser, eine eigene Datenstruktur zu erstellen.

Ich habe so etwas in meiner Bibliothek

public class Pair<First,Second>{.. }
Swapneel Patil
quelle
2

Sie können die AutoValue-Bibliothek von Google verwenden - https://github.com/google/auto/tree/master/value .

Sie erstellen eine sehr kleine abstrakte Klasse und kommentieren sie mit @AutoValue. Der Annotationsprozessor generiert für Sie eine konkrete Klasse mit einer Wertsemantik.

Shahar
quelle
2

Hier sind einige Bibliotheken mit mehreren Tupelgraden:

  • JavaTuples . Tupel vom Grad 1-10 sind alles, was es hat.
  • JavaSlang . Tupel vom Grad 0-8 und viele andere funktionale Extras.
  • jOOλ . Tupel vom Grad 0-16 und einige andere funktionale Extras. (Haftungsausschluss, ich arbeite für die Wartungsfirma)
  • Funktionelles Java . Tupel vom Grad 0-8 und viele andere funktionale Extras.

Es wurde erwähnt, dass andere Bibliotheken mindestens das PairTupel enthalten.

Insbesondere im Kontext der funktionalen Programmierung, bei der viel strukturelle Typisierung anstelle von nominaler Typisierung verwendet wird ( wie in der akzeptierten Antwort empfohlen ), sind diese Bibliotheken und ihre Tupel sehr praktisch.

Lukas Eder
quelle
2

eine weitere knappe Lombok-Implementierung

import lombok.Value;

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

Ich habe festgestellt, dass alle hier verstreuten Paarimplementierungen der Reihenfolge der beiden Werte Bedeutung zuschreiben. Wenn ich an ein Paar denke, denke ich an eine Kombination von zwei Elementen, bei denen die Reihenfolge der beiden keine Rolle spielt. Hier ist meine Implementierung eines ungeordneten Paares mit hashCodeund equalsÜberschreibungen, um das gewünschte Verhalten in Sammlungen sicherzustellen. Auch klonbar.

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

    }

}

Diese Implementierung wurde ordnungsgemäß Unit-getestet und die Verwendung in einem Set und einer Map wurde ausprobiert.

Beachten Sie, dass ich nicht behaupte, dies öffentlich zugänglich zu machen. Dies ist Code, den ich gerade für die Verwendung in einer Anwendung geschrieben habe. Wenn Sie ihn also verwenden möchten, machen Sie bitte keine direkte Kopie und spielen Sie ein wenig mit den Kommentaren und Namen herum. Fang meinen Drift?

G_H
quelle
3
Überprüfen Sie am Ende jeder Seite: "Benutzerbeiträge unter cc-wiki lizenziert"
amara
Ah, das hatte ich nicht bemerkt. Danke für die Warnung. Verwenden Sie in diesem Fall den Code, den Sie unter dieser Lizenz für richtig halten.
G_H
1
Die Frage bezieht sich auf ein C ++ - äquivalentes Paar - das geordnet ist. Ich denke auch, dass das Einfügen von Paaren in Sammlungen zu undefiniertem Verhalten führen kann, solange man einen Verweis auf das Objekt von Pair hat und diese veränderbar sind.
Mr_and_Mrs_D
1

Wenn jemand eine absolut einfache und benutzerfreundliche Version haben möchte, habe ich meine unter https://github.com/lfac-pt/Java-Pair verfügbar gemacht . Auch Verbesserungen sind sehr willkommen!

Luís Cardoso
quelle
1

com.sun.tools.javac.util.Pair ist eine einfache Implementierung eines Paares. Es befindet sich in jdk1.7.0_51 \ lib \ tools.jar.

Anders als das org.apache.commons.lang3.tuple.Pair ist es nicht nur eine Schnittstelle.

masterxilo
quelle
2
Niemand sollte jedoch die internen JDK-APIs verwenden.
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;
    }

}

Verwendungszweck :

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

Unveränderlich, nur ein Paar!

Bastiflew
quelle
Oh wow. Noch einer? Versuchen Sie, Ihre mit komplexeren Generika zu verwenden - irgendwann werden die entsprechenden Typen nicht mehr abgeleitet. Außerdem sollte Folgendes möglich sein: Pair<Object, Object> pair = Pair.createPair("abc", "def")Aber ich denke, man muss Pair.createPair((Object)"abc", (Object)"def")mit Ihrem Code schreiben ?
Hat aufgehört - Anony-Mousse
Sie können die statische Methode durch diese ersetzen: @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); } aber ich weiß nicht, ob es eine gute Praxis ist
Bastiflew
Nein, das wird die Dinge wahrscheinlich nur noch mehr vermasseln. Nach meiner Erfahrung wird sich mindestens einer der Compiler (versuchen Sie es mit Java6, Java7, Javadoc und Eclipse Java Compiler) beschweren. Das Traditionelle new Pair<Object, Object>("abc", "def")war das zuverlässigste in meinen Experimenten.
Hat aufgehört - Anony-Mousse