Zastanów się, List<String> stringListktóre można wydrukować na wiele sposobów za pomocą konstrukcji Java 8 :
stringList.forEach(System.out::println); // 1) Iterable.forEach
stringList.stream().forEach(System.out::println); // 2) Stream.forEach (order maintained generally but doc does not guarantee)
stringList.stream().forEachOrdered(System.out::println); // 3) Stream.forEachOrdered (order maintained always)
stringList.parallelStream().forEach(System.out::println); // 4) Parallel version of Stream.forEach (order not maintained)
stringList.parallelStream().forEachOrdered(System.out::println); // 5) Parallel version ofStream.forEachOrdered (order maintained always)
Czym różnią się od siebie te podejścia?
Pierwsze podejście ( Iterable.forEach) -
zazwyczaj używany jest iterator kolekcji, który jest zaprojektowany tak, aby działał szybko, co oznacza, że zostanie rzucony, ConcurrentModificationExceptionjeśli podstawowa kolekcja zostanie zmodyfikowana strukturalnie podczas iteracji. Jak wspomniano w dokumencie dla ArrayList:
Modyfikacja strukturalna to dowolna operacja, która dodaje lub usuwa jeden lub więcej elementów lub wyraźnie zmienia rozmiar tablicy pomocniczej; samo ustawienie wartości elementu nie jest modyfikacją strukturalną.
Oznacza to, że ArrayList.forEachustawienie wartości jest dozwolone bez żadnych problemów. A w przypadku równoczesnego gromadzenia, np ConcurrentLinkedQueue. Iterator byłby słabo spójny, co oznacza, że przekazane działania forEachmogą dokonywać nawet zmian strukturalnych bez ConcurrentModificationExceptionwyjątku. Ale tutaj modyfikacje mogą, ale nie muszą być widoczne w tej iteracji.
Drugie podejście ( Stream.forEach) -
Kolejność jest niezdefiniowana. Chociaż może nie wystąpić w przypadku strumieni sekwencyjnych, ale specyfikacja tego nie gwarantuje. Konieczne jest również, aby działanie nie miało charakteru zakłócającego. Jak wspomniano w dokumencie :
Zachowanie tej operacji jest wyraźnie niedeterministyczne. W przypadku potoków strumienia równoległego ta operacja nie gwarantuje przestrzegania kolejności spotkania strumienia, ponieważ spowodowałoby to poświęcenie korzyści z równoległości.
Trzecie podejście ( Stream.forEachOrdered) -
Akcja zostanie wykonana w kolejności spotkania strumienia. Tak więc zawsze, gdy liczy się kolejność, korzystaj forEachOrderedbez zastanowienia. Jak wspomniano w dokumencie :
Wykonuje akcję dla każdego elementu tego strumienia, w kolejności spotkania strumienia, jeśli strumień ma zdefiniowaną kolejność spotkań.
Podczas iteracji nad zsynchronizowanym kolekcji na pierwsze podejście byłoby wziąć blokadę kolekcji na raz i będzie trzymać go we wszystkich wywołań metody działania, ale w przypadku strumieni używają spliterator kolekcji, która nie blokuje i opiera się na już ustalonych zasad niedziałania -ingerencja. W przypadku zmodyfikowania kopii zapasowej strumienia podczas iteracji ConcurrentModificationExceptionzostanie wyrzucony lub może wystąpić niespójny wynik.
Czwarte podejście (równoległe Stream.forEach) -
jak już wspomniano, nie ma gwarancji przestrzegania kolejności spotkań zgodnie z oczekiwaniami w przypadku strumieni równoległych. Możliwe jest, że akcja jest wykonywana w innym wątku dla różnych elementów, co nigdy nie jest możliwe forEachOrdered.
Piąty Approach (równolegle Stream.forEachOrdered) -forEachOrdered przetwarza elementy w kolejności określonej przez źródła, niezależnie od tego, czy strumień jest kolejno lub równolegle. Dlatego nie ma sensu używać tego z równoległymi strumieniami.
List? Pokaż, jak to zadeklarowałeś i utworzyłeś instancję.