it-swarm.com.de

Hat Java SE 8 Paare oder Tupel?

Ich spiele mit faulen Funktionsoperationen in Java SE 8, und ich möchte map einen Index i auf ein Paar/Tupel (i, value[i]), dann filter basierend auf dem zweiten value[i] - Element und geben schließlich nur die Indizes aus.

Muss ich noch leiden: Was ist das Äquivalent des C++ - Paares <L, R> in Java? in der kühnen neuen Ära der Lambdas und Streams?

Update: Ich präsentierte ein ziemlich vereinfachtes Beispiel, das eine saubere Lösung von @dkatzel in einer der folgenden Antworten enthält. Es verallgemeinert jedoch nicht. Lassen Sie mich daher ein allgemeineres Beispiel hinzufügen:

package com.example.test;

import Java.util.ArrayList;
import Java.util.stream.IntStream;

public class Main {

  public static void main(String[] args) {
    boolean [][] directed_acyclic_graph = new boolean[][]{
        {false,  true, false,  true, false,  true},
        {false, false, false,  true, false,  true},
        {false, false, false,  true, false,  true},
        {false, false, false, false, false,  true},
        {false, false, false, false, false,  true},
        {false, false, false, false, false, false}
    };

    System.out.println(
        IntStream.range(0, directed_acyclic_graph.length)
        .parallel()
        .mapToLong(i -> IntStream.range(0, directed_acyclic_graph[i].length)
            .filter(j -> directed_acyclic_graph[j][i])
            .count()
        )
        .filter(n -> n == 0)
        .collect(() -> new ArrayList<Long>(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2))
    );
  }

}

Dies ergibt eine falsche Ausgabe von [0, 0, 0], Die der Anzahl für die drei Spalten entspricht, die alle sind false. Was ich brauche, sind die Indizes dieser drei Spalten. Die korrekte Ausgabe sollte [0, 2, 4] Sein. Wie kann ich dieses Ergebnis erhalten?

168
necromancer

UPDATE: Diese Antwort ist eine Antwort auf die ursprüngliche Frage: Hat Java SE 8 Paare oder Tupel? = (Und implizit, wenn nicht, warum nicht?) Das OP hat die Frage mit einem vollständigeren Beispiel aktualisiert, aber es scheint, als ob sie ohne Verwendung einer Art Paarstruktur gelöst werden kann. [Anmerkung von OP: hier ist die andere richtige Antwort ]


Die kurze Antwort lautet nein. Sie müssen entweder Ihre eigene rollen oder eine der verschiedenen Bibliotheken einbringen, die sie implementieren.

Eine Pair -Klasse in Java SE wurde mindestens einmal vorgeschlagen und abgelehnt. Siehe diesen Diskussionsthread auf einer der OpenJDK-Mailinglisten. Die Kompromisse sind nicht offensichtlich Einerseits gibt es viele Pair - Implementierungen in anderen Bibliotheken und im Anwendungscode. Dies zeigt, dass dies erforderlich ist, und das Hinzufügen einer solchen Klasse zu Java SE erhöht die Wiederverwendung und das Teilen Wenn Sie dagegen eine Pair-Klasse haben, wird die Versuchung verstärkt, komplizierte Datenstrukturen aus Paaren und Sammlungen zu erstellen, ohne die erforderlichen Typen und Abstraktionen zu erstellen. (Dies ist eine Umschreibung von Kevin Bourillions Botschaft aus diesem Thread.)

Ich empfehle jedem, den gesamten E-Mail-Thread zu lesen. Es ist bemerkenswert aufschlussreich und hat keine Flamme. Es ist ziemlich überzeugend. Als es anfing, dachte ich: "Ja, es sollte eine Pair-Klasse in Java SE" geben, aber als der Thread sein Ende erreichte, hatte ich meine Meinung geändert.

Beachten Sie jedoch, dass JavaFX die Klasse javafx.util.Pair hat. Die JavaFX-APIs wurden separat von den Java SE-APIs) entwickelt.

Wie man an der verknüpften Frage sehen kann Was ist das Äquivalent des C++ Pair in Java? Es gibt einen ziemlich großen Designraum, der eine scheinbar so einfache API umgibt. Sollten die Objekte unveränderlich sein? Sollten sie serialisierbar sein? Sollen sie vergleichbar sein? Sollte die Klasse endgültig sein oder nicht? Sollen die beiden Elemente bestellt werden? Sollte es eine Schnittstelle oder eine Klasse sein? Warum bei Paaren aufhören? Warum nicht Tripel, Quads oder N-Tupel?

Und natürlich gibt es die unvermeidliche Benennung der Elemente:

  • (a, b)
  • (erste Sekunde)
  • (links rechts)
  • (auto, cdr)
  • (foo, bar)
  • usw.

Ein großes Thema, das kaum erwähnt wurde, ist das Verhältnis von Paaren zu Primitiven. Wenn Sie ein (int x, int y) - Datum haben, das einen Punkt im 2D-Raum darstellt, werden bei der Darstellung als Pair<Integer, Integer> drei Objekte anstelle von zwei 32-Bit-Wörtern verwendet . Darüber hinaus müssen sich diese Objekte auf dem Heap befinden und verursachen einen GC-Overhead.

Es scheint klar zu sein, dass es wie bei Streams unbedingt primitive Spezialisierungen für Paare geben muss. Wollen wir sehen:

Pair
ObjIntPair
ObjLongPair
ObjDoublePair
IntObjPair
IntIntPair
IntLongPair
IntDoublePair
LongObjPair
LongIntPair
LongLongPair
LongDoublePair
DoubleObjPair
DoubleIntPair
DoubleLongPair
DoubleDoublePair

Sogar ein IntIntPair würde immer noch ein Objekt auf dem Heap benötigen.

Diese erinnern natürlich an die Verbreitung funktionaler Schnittstellen im Paket Java.util.function In Java SE 8). Wenn Sie keine aufgeblähte API möchten, welche würden Sie Sie könnten auch argumentieren, dass dies nicht ausreicht und dass auch Spezialisierungen für beispielsweise Boolean hinzugefügt werden sollten.

Mein Gefühl ist, dass wenn Java vor langer Zeit eine Pair-Klasse hinzugefügt hätte, es einfach oder sogar simpel gewesen wäre und es nicht viele der Anwendungsfälle befriedigt hätte, die wir uns jetzt vorstellen. Wenn Pair im JDK 1.0-Zeitrahmen hinzugefügt worden wäre, wäre es wahrscheinlich veränderlich gewesen! (Siehe Java.util.Date.) Wären die Leute damit zufrieden gewesen? Ich schätze, wenn es eine Pair-Klasse in gäbe Java, es wäre irgendwie nicht wirklich nützlich, und jeder würde immer noch seine eigenen Rollen spielen, um seine Bedürfnisse zu befriedigen. Es gäbe verschiedene Pair- und Tuple-Implementierungen in externen Bibliotheken, und die Leute würden immer noch darüber streiten/diskutieren, wie es geht Repariere Javas Pair Klasse, mit anderen Worten, irgendwie an dem Ort, an dem wir uns heute befinden.

In der Zwischenzeit werden einige Arbeiten zur Behebung des Grundproblems durchgeführt, das in der JVM (und möglicherweise in der Sprache Java) für Werttypen besser unterstützt wird. Siehe hierzu Zustand der Werte Dokument. Dies ist eine vorläufige, spekulative Arbeit, die nur Fragen aus der Perspektive der JVM abdeckt, aber bereits eine Menge Gedanken hinter sich hat. Natürlich gibt es keine Garantie dafür, dass dies in Java 9) oder irgendwohin gelangen wird, aber es zeigt die aktuelle Denkrichtung zu diesem Thema.

187
Stuart Marks

Sie können einen Blick auf diese integrierten Klassen werfen:

37
senerh

Leider hat Java 8 keine Paare oder Tupel eingeführt. Sie können natürlich immer org.Apache.commons.lang3.Tuple verwenden (was ich persönlich in Kombination verwende mit Java 8) oder Sie können Ihre eigenen Wrapper erstellen oder Maps verwenden oder ähnliches, wie in akzeptierte Antwort zu dieser Frage, mit der Sie verlinkt sind, erklärt .

20
blalasaadri

Es scheint, dass das vollständige Beispiel ohne die Verwendung einer beliebigen Paarstruktur gelöst werden kann. Der Schlüssel besteht darin, nach den Spaltenindizes zu filtern, wobei das Prädikat die gesamte Spalte überprüft, anstatt die Spaltenindizes der Anzahl von false Einträgen in dieser Spalte zuzuordnen.

Der Code, der dies tut, ist hier:

    System.out.println(
        IntStream.range(0, acyclic_graph.length)
            .filter(i -> IntStream.range(0, acyclic_graph.length)
                                  .noneMatch(j -> acyclic_graph[j][i]))
            .boxed()
            .collect(toList()));

Dies führt zur Ausgabe von [0, 2, 4], Was meines Erachtens das korrekte vom OP angeforderte Ergebnis ist.

Beachten Sie auch die boxed() -Operation, die die int -Werte in Integer -Objekte einfügt. Dies ermöglicht es einem, den bereits vorhandenen toList() -Kollektor zu verwenden, anstatt Kollektorfunktionen zu schreiben, die das Boxen selbst ausführen.

14
Stuart Marks

Da Sie sich nur um die Indizes kümmern, müssen Sie die Tupel überhaupt nicht zuordnen. Warum nicht einfach einen Filter schreiben, der die Look-Up-Elemente in Ihrem Array verwendet?

     int[] value =  ...


IntStream.range(0, value.length)
            .filter(i -> value[i] > 30)  //or whatever filter you want
            .forEach(i -> System.out.println(i));
6
dkatzel

Ja.

Map.Entry kann als Pair verwendet werden.

Leider hilft es bei Java 8-Streams nicht, da das Problem darin besteht, dass Lambdas zwar mehrere Argumente annehmen können, die Sprache Java nur einen einzigen Wert zurückgeben kann (Objekt- oder primitiver Typ.) Dies impliziert, dass Sie immer dann, wenn Sie einen Stream haben, ein einzelnes Objekt aus der vorherigen Operation übergeben bekommen. Dies ist ein Mangel in der Sprache Java=, weil wenn mehrere Rückgabewerte wurden unterstützt UND Streams unterstützten sie. Wir könnten viel schönere, nicht triviale Aufgaben haben, die von Streams ausgeführt werden.

Bis dahin nützt es nur wenig.

BEARBEITEN 2018-02-12: Während ich an einem Projekt arbeitete, habe ich eine Hilfsklasse geschrieben, die dabei hilft, den Sonderfall zu behandeln, dass ein Bezeichner zu einem späteren Zeitpunkt im Stream vorhanden ist, der dazwischen liegende Stream-Teil jedoch nichts davon weiß. Bis ich es alleine herausbringe, ist es verfügbar unter IdValue.Java mit einem Unit-Test unter IdValueTest.Java

Vavr (früher Javaslang genannt) ( http://www.vavr.io ) liefert Tupel (bis zu einer Größe von 8) auch. Hier ist das Javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .

Dies ist ein einfaches Beispiel:

Tuple2<Integer, String> entry = Tuple.of(1, "A");

Integer key = entry._1;
String value = entry._2;

Warum JDK selbst bisher nicht mit einer einfachen Art von Tupeln geliefert wurde, ist mir ein Rätsel. Wrapper-Klassen zu schreiben scheint ein alltägliches Geschäft zu sein.

4
wumpz

Seit Java 9 können Sie einfacher als bisher Instanzen von Map.Entry Erstellen:

Entry<Integer, String> pair = Map.entry(1, "a");

Map.entry gibt ein nicht änderbares Entry zurück und verbietet Nullen.

3
ZhekaKozlov

Eclipse Collections enthält Pair und alle Kombinationen von Primitiv-/Objektpaaren (für alle acht Primitive).

Die Tuples -Factory kann Instanzen von Pair erstellen, und die PrimitiveTuples -Factory kann verwendet werden, um alle Kombinationen von zu erstellen Primitiv/Objekt-Paare.

Wir haben diese hinzugefügt, bevor Java 8 veröffentlicht wurde. Sie waren nützlich, um Schlüssel/Wert-Iteratoren für unsere primitiven Maps zu implementieren, die wir auch in allen primitiven/Objekt-Kombinationen unterstützen.

Wenn Sie den zusätzlichen Bibliotheksaufwand hinzufügen möchten, können Sie die von Stuart akzeptierte Lösung verwenden und die Ergebnisse in einem primitiven IntList zusammenfassen, um Boxen zu vermeiden. Wir haben in Eclipse Collections 9. neue Methoden hinzugefügt, damit Int/Long/Double - Sammlungen aus Int/Long/Double - Streams erstellt werden können.

IntList list = IntLists.mutable.withAll(intStream);

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.

2
Donald Raab

Ich bin kein Java-Experte, nur ein Student, aber ich habe meine Problemumgehung gemacht, um die Tupel wie in Python zu simulieren:

class C1 { 
    String name; int X,Y;
    C1(List l){name = ""+l.get(0); X=(int)l.get(1); Y=(int)l.get(2);}
}

class MyClass{
    List l2= List.of(List.of("a",7,9), List.of("b",8,4), List.of("c",3,6)); //here
    List l3=new ArrayList();
    for (Object li: l2) l3.add(new C1((List)li));
}

Hoffe das hilft.

0
Carlos Leandro