2016-10-09 20 views
5

W oficjalnej dokumentacji można przeczytać, że:Co oznacza cecha UNORDERED kolekcjonera Java 8?

UNORDERED wskazuje, że operacja kolekcja nie zobowiązuje do zachowując kolejność spotkanie elementów wejściowych.

To nie jest zbyt pomocne bez żadnych przykładów.

Moje pytanie brzmi, co dokładnie oznacza charakterystyczna cecha UNORDERED? Czy powinienem używać go z redukcją kolektorów, takich jak min lub suma, czy też dotyczy to tylko kolektorów?

W OpenJDK wygląda jak operacje zmniejszające (min, sum, avg) mają puste charakterystyki. Spodziewałem się znaleźć tam co najmniej CONCURRENT i UNORDERED.

+0

'CONCURRENT' nie oznacza, co Twoim zdaniem oznacza. –

+3

UNORDERED oznacza po prostu nie zamówione. Nie ma gwarancji, jaką kolejność uzyskasz. –

+1

* "Nie jest to zbyt pomocne bez żadnych przykładów." * - ograniczasz swoją potencjalną przestrzeń odpowiedzi, oczekując próbek kodu. Wiele specyfikacji i dokumentacji pojawia się tylko w prozie. – the8472

Odpowiedz

5

UNORDERED zasadniczo oznacza, że ​​kolektor jest zarówno asocjacyjny (wymagany przez specyfikację), jak i przemienny (niewymagany).

Skojarzenie umożliwia dzielenie obliczeń na podelementy, a następnie łączenie ich w pełny wynik, ale wymaga ścisłego uporządkowania etapu łączenia. Zbadać ten fragment z docs:

A a2 = supplier.get(); 
accumulator.accept(a2, t1); 
A a3 = supplier.get(); 
accumulator.accept(a3, t2); 
R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting 

w ostatnim etapie, combiner.apply(a2, a3) argumenty muszą pojawiać się w dokładnie tej kolejności, co oznacza, że ​​cała obliczenie rurociąg musi śledzić kolejność i szanować go w końcu.

Innym sposobem powiedzenia tego jest to, że drzewo, które otrzymujemy z podziału rekursywnego, musi zostać uporządkowane.

Z drugiej strony, jeśli operacja łączenia jest przemienna, możemy łączyć dowolne inne części w dowolnej kolejności i zawsze uzyskiwać taki sam wynik. Oczywiście prowadzi to do wielu możliwości optymalizacji zarówno w wymiarze przestrzennym, jak i czasowym.

Należy zauważyć, że w JDK znajdują się kolektory UNORDERED, które nie gwarantują przemienności. Główną kategorią są kolektory "wyższego rzędu", które składają się z innych kolektorów niższego rzędu, ale nie wymuszają na nich właściwości UNORDERED.

+0

Byłbym ostrożny, mówiąc, że "UNORDERED" oznacza przemienność. Nie wymaga, aby zamienione zamówienie dawało taki sam wynik, może to oznaczać, że różnica nie ma znaczenia. – Holger

+0

Czekałem na ciebie, Holger ☺ Moim zdaniem "różnica nie ma znaczenia" jest definiującą właściwością "tego samego", ze wszystkich praktycznych powodów. –

+0

Akceptuję ten punkt widzenia, ale nie jestem pewien, czy czytelnik rozumie, że 'Collectors.toList()' jest przemienne, gdy jest używane w ramach 'groupingByConcurrent', ale nie jest używane w' groupingBy', jak zwykle, komutatywna właściwość funkcji jest niezmienna. – Holger

3

Sama klasa wewnętrzna Collector.Characteristics jest dość lakoniczny w jego opisie, ale jeśli spędzić kilka sekund zbadanie kontekstu, można zauważyć, że zawierający Collector interfejs zapewnia dodatkowe informacje

Dla kolekcjonerów, które nie mają Charakterystyka UNORDERED, dwa skumulowane wyniki a1 i a2 są równoważne, jeśli finisher.apply (a1) .equals (finisher.apply (a2)). W przypadku kolektorów nieuporządkowanych równoważność zostaje złagodzona, aby umożliwić nierówność związaną z różnicami w porządku. (Na przykład, nieuporządkowana kolektor, który zgromadził elementy do listy byłoby rozważyć dwie listy równoważne, jeżeli zawierają one te same elementy, kolejność ignorując.)


W OpenJDK wygląda zmniejszenie operacji (min, suma , śr.) mają pustą charakterystykę, spodziewałem się znaleźć tam co najmniej KONKURENCYJNY i NIEOGRANICZONY.

Przynajmniej dla podwójnego sumowania i średnich są zdecydowanie uporządkowane, a nie współbieżne, ponieważ logika sumowania wykorzystuje łączenie podporządkowane, a nie bezpiecznik wątkowy.

+0

Masz na myśli, że mogą być np. przepełnienie, gdy zamówienie nie jest zachowywane podczas sumowania - ok, otrzymuję je teraz – csharpfolk

+4

Arytmetyka zmiennoprzecinkowa o precyzyjnej precyzji jest ogólnie nieprzemienna, nie tylko z powodu nasycenia do nieskończoności. – the8472

10

W przypadku braku specjalnego pisma proces operacji strumieniowych musi zachowywać się tak, jakby elementy były przetwarzane w kolejności spotkań źródła. W przypadku niektórych operacji - takich jak redukcja z operacją asocjacyjną - można przestrzegać tego ograniczenia i nadal uzyskać wydajne wykonanie równoległe. Dla innych ograniczenie to jest bardzo ograniczone. W przypadku niektórych problemów to ograniczenie nie ma znaczenia dla użytkownika. Rozważmy następujący Stream:

people.stream() 
     .collect(groupingBy(Person::getLastName, 
          mapping(Person::getFirstName)); 

ważne jest, że lista imion związanych z „Smith” pojawi się na mapie w kolejności, w jakiej pojawiła się w początkowym strumieniu? Dla niektórych problemów, tak, dla niektórych nie - nie chcemy, aby biblioteka strumieniowa zgadywała dla nas. Zamówiony kolektor mówi, że w porządku jest wstawianie pierwszych imion do listy w porządku niezgodnym z kolejnością, w jakiej Smith-owniacy ludzie pojawiają się w źródle wejściowym. Rozluźniając to ograniczenie, czasami (nie zawsze) biblioteka strumieniowa może dać bardziej wydajne wykonanie.

Na przykład, jeśli nie dbają o zachowanie tej kolejności, można ją wykonać jako:

people.parallelStream() 
     .collect(groupingByConcurrent(Person::getLastName, 
            mapping(Person::getFirstName)); 

Jednoczesne kolektor jest nieuporządkowana, które pozwala na optymalizację dzielenie stanowiącego podstawę ConcurrentMap, zamiast O(log n) kroki scalania map. Rozluźnienie ograniczenia zamawiania zapewnia realną przewagę algorytmiczną - ale nie możemy zakładać, że ograniczenie nie ma znaczenia, potrzebujemy, aby użytkownik nas o tym poinformował. Używanie kolektora UNORDERED jest jednym ze sposobów poinformowania biblioteki strumieniowej, że te optymalizacje są uczciwą grą.

+2

To nie odpowiada na pytanie, dlaczego wbudowane kolektory, takie jak 'summingInt', nie mają cechy" ZWOLNIONE "... – Holger

+2

To mylące powiedzenie" Kolektory takie jak 'summingInt' respektują kolejność spotkań", ponieważ to w rzeczywistości implementacja Stream, która jest zmuszony uszanować kolejność spotkań, ponieważ kolektor nie zgłasza "UNORDERED". Zatem kolekcjoner nie powinien decydować, że potencjalne oszczędności w wysiłku Stream nie przyniosą żadnych korzyści. W jaki sposób Collector może o tym wiedzieć? Nawet, jeśli dzisiaj nie ma żadnej korzyści *, zapytajmy odwrotnie: jaka jest korzyść z * nie * zgłaszania cech charakterystycznych "UNORDERED"? To tylko decyzja o przekazaniu konstruktorowi 'CH_NOID' lub' CH_UNORDERED_ID' ... – Holger