Treffen Sie freundlich / unfreundlich terminal Operationen gegen parallel / sequentiell vs geordnete / ungeordnete Streams

8

Inspiriert von dieser Frage begann ich um mit geordneten vs ungeordneten Streams zu spielen, parallele vs sequentielle Streams und terminale Operationen, die die Reihenfolge der Begegnung gegen terminale Operationen respektieren, die sie nicht respektieren.

In einer Antwort auf die verknüpfte Frage wird ein Code ähnlich dem folgenden angezeigt:

%Vor%

Und die Listen sind in der Tat unterschiedlich. Die unordered -Liste ändert sich sogar von einem Lauf zum nächsten und zeigt, dass das Ergebnis tatsächlich nicht deterministisch ist.

Also habe ich dieses andere Beispiel erstellt:

%Vor%

Und ich habe erwartet, ähnliche Ergebnisse zu sehen, da der Stream sowohl parallel als auch ungeordnet ist (vielleicht ist unordered() redundant, da es bereits parallel ist). Die resultierende Liste ist jedoch geordnet, d. H. Sie ist gleich der Quellenliste.

Also meine Frage ist, warum die gesammelten Liste bestellt ist? Beherzt collect die Begegnungs- Reihenfolge auch für parallele, ungeordnete Streams? Ist es der spezifische Collectors.toCollection(...) -Kollektor, der die Begegnung-Reihenfolge erzwingt?

    
Federico Peralta Schaffner 06.07.2017, 16:29
quelle

3 Antworten

6

Collectors.toCollection gibt eine Collector zurück, der die Eigenschaft Collector.Characteristics.UNORDERED fehlt. Ein anderer Kollektor, der Collector.Characteristics.UNORDERED angegeben hat, verhält sich möglicherweise anders.

Das heißt: "ungeordnet" bedeutet keine Garantien , nicht garantiert zu variieren. Wenn die Bibliothek es am einfachsten findet, eine ungeordnete Sammlung wie bestellt zu behandeln, ist es erlaubt tun Sie es, und dieses Verhalten darf die Veröffentlichung freilassen, dienstags oder wenn Vollmond ist.

(Beachten Sie auch, dass Collectors.toCollection nicht die Verwendung einer parallelen Auflistungsimplementierung erfordert, wenn Sie parallele Datenströme verwenden; toCollection(ArrayList::new) würde funktionieren. Das liegt daran, dass der Kollektor nicht das Merkmal Collector.Characteristics.CONCURRENT hat Daher wird eine Sammlungsstrategie verwendet, die auch bei parallelen Streams für nicht gleichzeitige Sammlungen verwendet wird.)

Wenn Sie einen ungeordneten Stream verwenden, aber einen Collector, der nicht UNORDERED ist, oder umgekehrt, bezweifle ich, dass Sie vom Framework irgendwelche Garantien erhalten. Wenn es einen Tisch gäbe, würde es heißen: "Here DRAGONS UNDEFINED VERHALTEN." Ich würde hier auch einige Unterschiede für verschiedene Arten von verketteten Operationen erwarten, z. Eugene Erwähnungen findFirst variiert hier, obwohl findFirst inhärent eine geordnete Operation ist - unordered().findFirst() wird äquivalent zu findAny() .

Für Stream.collect glaube ich, dass die aktuelle Implementierung drei Strategien hat, zwischen denen sie wählt:

  • Sequential: Startet einen Akkumulator, akkumuliert Elemente in ihm (in der Reihenfolge des Aufeinandertreffens, denn warum machst du Mühe, die Elemente zu mischen? akzeptiere sie einfach in der Reihenfolge, in der du sie bekommst), ruft der Finisher auf.
  • Parallele Ausführung, gleichzeitiger Kollektor und der Stream oder der Kollektor sind ungeordnet: ein Akkumulator, zerlegt die Eingabe, Worker-Threads verarbeiten die Elemente von jedem Shard und fügen Elemente zum Akkumulator hinzu, wenn sie fertig sind, ruft den Finisher auf.
  • Parallele Ausführung, alles andere: zerlegt den Input in N Shards, jeder Shard wird sequentiell in seinem eigenen distinct Akkumulator akkumuliert, die Akkumulatoren werden mit der Combiner Funktion kombiniert, ruft den Finisher auf.
Louis Wasserman 06.07.2017, 16:37
quelle
4

Unter der aktuellen Implementierung habe ich java-8 und java-9 überprüft, dass das ungeordnete Flag für die collect -Phase für nicht-gleichzeitige Kollektoren ignoriert wird ( Collector.Characteristics.UNORDERED ist nicht gesetzt). Die Implementierungen dürfen das tun, irgendwie ähnlich Frage .

In derselben Frage, die Sie verlinkt haben, habe ich ein Beispiel dafür gegeben, wie findFirst tatsächlich von jdk-8 in jdk-9 geändert wurde.

    
Eugene 06.07.2017 16:45
quelle
1
___ qstnhdr ___ Treffen Sie freundlich / unfreundlich terminal Operationen gegen parallel / sequentiell vs geordnete / ungeordnete Streams ___ answer44954598 ___

Stream#collect gibt eine split zurück, der die Eigenschaft merge fehlt. Ein anderer Kollektor, der Collectors#toList angegeben hat, verhält sich möglicherweise anders.

Das heißt: "ungeordnet" bedeutet keine Garantien , nicht garantiert zu variieren. Wenn die Bibliothek es am einfachsten findet, eine ungeordnete Sammlung wie bestellt zu behandeln, ist es erlaubt tun Sie es, und dieses Verhalten darf die Veröffentlichung freilassen, dienstags oder wenn Vollmond ist.

(Beachten Sie auch, dass %code% nicht die Verwendung einer parallelen Auflistungsimplementierung erfordert, wenn Sie parallele Datenströme verwenden; %code% würde funktionieren. Das liegt daran, dass der Kollektor nicht das Merkmal %code% hat Daher wird eine Sammlungsstrategie verwendet, die auch bei parallelen Streams für nicht gleichzeitige Sammlungen verwendet wird.)

Wenn Sie einen ungeordneten Stream verwenden, aber einen Collector, der nicht %code% ist, oder umgekehrt, bezweifle ich, dass Sie vom Framework irgendwelche Garantien erhalten. Wenn es einen Tisch gäbe, würde es heißen: "Here DRAGONS UNDEFINED VERHALTEN." Ich würde hier auch einige Unterschiede für verschiedene Arten von verketteten Operationen erwarten, z. Eugene Erwähnungen %code% variiert hier, obwohl %code% inhärent eine geordnete Operation ist - %code% wird äquivalent zu %code% .

Für %code% glaube ich, dass die aktuelle Implementierung drei Strategien hat, zwischen denen sie wählt:

  • Sequential: Startet einen Akkumulator, akkumuliert Elemente in ihm (in der Reihenfolge des Aufeinandertreffens, denn warum machst du Mühe, die Elemente zu mischen? akzeptiere sie einfach in der Reihenfolge, in der du sie bekommst), ruft der Finisher auf.
  • Parallele Ausführung, gleichzeitiger Kollektor und der Stream oder der Kollektor sind ungeordnet: ein Akkumulator, zerlegt die Eingabe, Worker-Threads verarbeiten die Elemente von jedem Shard und fügen Elemente zum Akkumulator hinzu, wenn sie fertig sind, ruft den Finisher auf.
  • Parallele Ausführung, alles andere: zerlegt den Input in N Shards, jeder Shard wird sequentiell in seinem eigenen distinct Akkumulator akkumuliert, die Akkumulatoren werden mit der Combiner Funktion kombiniert, ruft den Finisher auf.
___ answer44954708 ___

Unter der aktuellen Implementierung habe ich java-8 und java-9 überprüft, dass das ungeordnete Flag für die %code% -Phase für nicht-gleichzeitige Kollektoren ignoriert wird ( %code% ist nicht gesetzt). Die Implementierungen dürfen das tun, irgendwie ähnlich Frage .

In derselben Frage, die Sie verlinkt haben, habe ich ein Beispiel dafür gegeben, wie %code% tatsächlich von jdk-8 in jdk-9 geändert wurde.

    
___ tag123javastream ___ Verwenden Sie dieses Tag für Fragen zur Verwendung der Stream-API. Es wurde in Java 8 eingeführt und unterstützt funktionale Operationen für Datenströme, z. B. Filter-Map-Reduce-Pipelines in Sammlungen. ___ tag123java8 ___ Verwenden Sie dieses Tag für spezifische Fragen zu Java 8, Version 8 (interne Nummer 1.8) der Java-Plattform, die am 18. März 2014 veröffentlicht wurde. In den meisten Fällen sollten Sie auch das Java-Tag angeben. ___ tag123java ___ Java (nicht zu verwechseln mit JavaScript oder JScript oder JS) ist eine universelle objektorientierte Programmiersprache, die für die Verwendung in Verbindung mit der Java Virtual Machine (JVM) entwickelt wurde. "Java-Plattform" ist der Name für ein Computersystem, auf dem Tools zum Entwickeln und Ausführen von Java-Programmen installiert sind. Verwenden Sie dieses Tag für Fragen, die sich auf die Java-Programmiersprache oder Java-Plattform-Tools beziehen. ___ qstntxt ___

Inspiriert von dieser Frage begann ich um mit geordneten vs ungeordneten Streams zu spielen, parallele vs sequentielle Streams und terminale Operationen, die die Reihenfolge der Begegnung gegen terminale Operationen respektieren, die sie nicht respektieren.

In einer Antwort auf die verknüpfte Frage wird ein Code ähnlich dem folgenden angezeigt:

%Vor%

Und die Listen sind in der Tat unterschiedlich. Die %code% -Liste ändert sich sogar von einem Lauf zum nächsten und zeigt, dass das Ergebnis tatsächlich nicht deterministisch ist.

Also habe ich dieses andere Beispiel erstellt:

%Vor%

Und ich habe erwartet, ähnliche Ergebnisse zu sehen, da der Stream sowohl parallel als auch ungeordnet ist (vielleicht ist %code% redundant, da es bereits parallel ist). Die resultierende Liste ist jedoch geordnet, d. H. Sie ist gleich der Quellenliste.

Also meine Frage ist, warum die gesammelten Liste bestellt ist? Beherzt %code% die Begegnungs- Reihenfolge auch für parallele, ungeordnete Streams? Ist es der spezifische %code% -Kollektor, der die Begegnung-Reihenfolge erzwingt?

    
___ answer4955315 ___

die Dokumentation Stream # collect hat bereits erwähnt:

  

Bei einer parallelen Ausführung können mehrere Zwischenergebnisse instanziiert , gefüllt und zusammengeführt sein, um die Isolation veränderbarer Datenstrukturen aufrechtzuerhalten. Daher ist keine zusätzliche Synchronisation für eine parallele Reduktion erforderlich, selbst wenn sie parallel mit nicht threadsicheren Datenstrukturen (z. B. ArrayList) ausgeführt wird.

was bedeutet, dass %code% zwei primäre Dinge tut: %code% & amp; %Code%.

aber ich habe ein spezielles Beispiel in jdk-8, dass Sie die verschiedenen Ergebnisse abrufen können, :). wenn ein ungeordneter Stream erstellt wird, Stream # generieren , dann können Sie verschiedene Ergebnisse für %code% abrufen, zum Beispiel:

%Vor% %Vor%     
___
holi-java 06.07.2017 17:18
quelle

Tags und Links