2017-02-08 3 views
7

ich einen Code haben setzen sich wie folgt:Java-Streams Mit dem letzten begegnet Wert in einer Karte

Map<RiskFactor, RiskFactorChannelData> updateMap = 
    updates.stream().filter(this::updatedValueIsNotNull). // Remove null updated values 
     collect(Collectors.toMap(
      u -> u.getUpdatedValue().getKey(), // then merge into a map of key->value. 
      Update::getUpdatedValue, 
      (a, b) -> b)); // If two values have the same key then take the second value 

Insbesondere möchte ich die Werte aus der Liste nehmen und sie in die Karte setzen. Das alles funktioniert perfekt. Meine Sorge ist jedoch mit der Bestellung.

Zum Beispiel, wenn die Liste hat:

a1, b1, a2 

Wie stelle ich sicher, dass die endgültige Karte enthält:

a->a2 
b->b1 

Statt

a->a1 
b->b1 

Die Eingangsliste wird bestellt, stream().filter() sollte die Bestellung beibehalten haben, aber ich kann nichts in der Dokumentation vonsehenüber die Reihenfolge der Eingänge.

Ist das im allgemeinen Fall sicher oder habe ich gerade Glück auf meinen Testfällen gehabt? Werde ich JVM-abhängig und in Gefahr sein, dass sich das in Zukunft ändert?

Dies ist sehr einfach zu garantieren, wenn ich nur eine for Schleife schreiben, aber die "Fuzzyness" des potenziellen Stream-Verhalten macht mich besorgt.

Ich plane nicht, dafür parallel zu verwenden, ich versuche nur, das Verhalten im Falle eines sequentiellen nichtparallelen Streams zu verstehen, der toMap erreicht.

+0

Ich weiß nicht, ob es sicher ist, aber wenn es nicht ist, könnten Sie 'Collectors.groupingBy' vor dem' Collectors.toMap' verwenden – Aaron

+0

Mögliche Duplikate: http://StackOverflow.com/questions/30258566/java- stream-map-and-collect-order-of-resulting-container – assylias

+0

Oder vielleicht besser: http://stackoverflow.com/a/30530572/829571 – assylias

Antwort

4

Der Begriff "letzter Wert" ist ein bisschen irreführend. Da Sie den letzten Wert gemäß der Reihenfolge der Begegnungen haben möchten, lautet die Antwort, dass toMap die Reihenfolge der Begegnungen respektiert.

Its documentation bezieht sich auf Map.merge, um die Semantik der Zusammenführungsfunktion zu erklären, aber leider ist diese Dokumentation auch ein wenig dünn. Es wird nicht erwähnt, dass diese Funktion explizit mit (oldValue,newValue) aufgerufen wird; Es kann nur aus dem Codebeispiel abgeleitet werden.

toMap’s documentation weiter heißt es:

Die zurück Collector ist nicht gleichzeitig.Für parallele Stream-Pipelines funktioniert die combiner-Funktion, indem die Schlüssel von einer Map in eine andere verschmelzen, was eine teure Operation sein kann. Wenn es nicht erforderlich ist, dass die Ergebnisse in der Reihenfolge "0" in "Map" zusammengefasst werden, kann die Verwendung von toConcurrentMap(Function, Function, BinaryOperator, Supplier) eine bessere parallele Leistung bieten.

Es leitet also explizit an einen anderen Sammler, wenn die Reihenfolge der Begegnung nicht erforderlich ist. Im Allgemeinen sind alle eingebauten Kollektoren, die von Collectors bereitgestellt werden, nur dann ungeordnet, wenn dies explizit angegeben ist, was nur für die Kollektoren "... Concurrent ..." und toSet() der Fall ist.

2

Es ist sicher, Collection.stream() erstellt einen sequentiellen Stream.

Ich schlage vor, einen Blick auf Collectors.toMap werfen im Falle von Kollisionen ist es wichtig, den richtigen Wert zu wählen. In Ihrem Fall sollten Sie die neueren verwenden.

Der Teil, an dem Sie interessiert sind, ist (a, b) -> b, wo Sie das zweite Element willkürlich wählen, dort sollten Sie die neueren wählen.

Ich denke, Ihre Probleme aus der Tatsache kam, die nicht sicher über die Verarbeitungsreihenfolge sind, im Fall, dass Sie weiterhin Ströme verwenden (anstelle einer for-Schleife) Sie diesen Zustand Zugabe .sequential() nach .stream() erzwingen könnte.

Eine andere Möglichkeit, würde ich bevorzugen, ist ein Zeitstempel zu RiskFactorChannelData hinzufügen, und verwenden Sie sogar einen parallelen Strom.

+0

richtig, aber die einzige Art, wie ich die neuere kenne, ist durch ihre Position in der ursprünglichen Anordnung. –

+0

@TimB Streams, die von einem Array erstellt werden, werden in der Reihenfolge des Arrays durchlaufen, ich denke, das ist eine gute Antwort. – Eugene

+0

@TimB, in diesem Fall, ich nehme an, Sie können Streams nicht für das verwenden, was Sie eigentlich wollen. Stellen Sie sich vor, Sie würden 'parallelStream' dort hinstellen, um es zu beschleunigen oder was auch immer - das bedeutet, dass Ihr Stream ungeordnet wird. Ich schlage vor, dass Sie Ihrem Update etwas wie einen Zeitstempel hinzufügen, um die tatsächliche Reihenfolge anzugeben, damit Sie sich nicht auf die Array-Position verlassen. –

Verwandte Themen