Comprendere Spliterator, Collector e Stream in Java 8


143

Ho difficoltà a comprendere l' Streaminterfaccia in Java 8, soprattutto dove ha a che fare con le interfacce Spliteratore Collector. Il mio problema è che semplicemente non riesco ancora a capire Spliteratore le Collectorinterfacce e, di conseguenza, l' Streaminterfaccia è ancora un po 'oscura per me.

Cos'è esattamente un Spliteratore un Collectore come posso usarli? Se sono disposto a scrivere il mio Spliteratoro Collector(e probabilmente il mio Streamin quel processo), cosa dovrei fare e non fare?

Ho letto alcuni esempi sparsi sul Web, ma poiché tutto qui è ancora nuovo e soggetto a modifiche, esempi e tutorial sono ancora molto scarni.

Risposte:


142

Quasi certamente non dovresti mai avere a che fare con Spliteratorun utente; dovrebbe essere necessario solo se stai scrivendo i Collectiontipi da solo e intendi anche ottimizzare operazioni parallele su di essi.

Per quello che vale, a Spliteratorè un modo di operare sugli elementi di una raccolta in modo che sia facile dividere parte della raccolta, ad esempio perché stai parallelizzando e vuoi che un thread funzioni su una parte della raccolta, un thread per lavorare su un'altra parte, ecc.

In sostanza, non dovresti mai salvare valori di tipo Streamin una variabile. Streamè un po 'come un Iterator, in quanto è un oggetto monouso che userai quasi sempre in una catena fluente, come nell'esempio Javadoc:

int sum = widgets.stream()
                  .filter(w -> w.getColor() == RED)
                  .mapToInt(w -> w.getWeight())
                  .sum();

Collectorè la versione più generalizzata e astratta possibile di un'operazione di "riduzione" sulla mappa / riduzione; in particolare, deve supportare le fasi di parallelizzazione e finalizzazione. Esempi di Collectors includono:

  • somma, ad es Collectors.reducing(0, (x, y) -> x + y)
  • StringBuilder in aggiunta, ad es Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)

31
Spliterator (s) fornisce anche un modo per trasmettere in streaming un Iterable che non è una raccolta
Bohemian

2
Intendevo "un'operazione di riduzione, nel senso che il termine è inteso in mappa / riduzione"
Louis Wasserman,

1
È Collectors.ofstato rimosso un vecchio metodo di versione beta o mi manca qualcosa? Per completezza, (x,y) -> x+ypuò essere scritto come Integer::sum.
Jean-François Savard,

3
Ehm, no, scusa, è Collector.of, non Collectors.of.
Louis Wasserman,

2
Il tuo esempio di Collezionista sarebbe più utile se spiegassi cosa fa ciascuno dei tuoi Collezionisti.
MiguelMunoz

90

Spliterator in pratica significa "Iteratore divisibile".

Il singolo thread può attraversare / elaborare l'intero Spliterator stesso, ma anche il Spliterator ha un metodo trySplit() che "divide" una sezione che qualcun altro (in genere un altro thread) elabora, lasciando lo splitterator corrente con meno lavoro.

Collectorcombina la specifica di una reducefunzione (di fama di riduzione della mappa), con un valore iniziale e una funzione per combinare due risultati (consentendo così di combinare i risultati di flussi di lavoro suddivisi).

Ad esempio, il Collector più semplice avrebbe un valore iniziale di 0, aggiungere un numero intero a un risultato esistente e "combinare" due risultati aggiungendoli. Sommando così un flusso diviso di numeri interi.

Vedere:


un valore per combinare due risultati?
Jason Law l'

@JasonLaw - chiarito! Grazie per il suggerimento
Thomas W

5

Di seguito sono riportati esempi di utilizzo dei collector predefiniti per eseguire attività di riduzione mutabili comuni:

 // Accumulate names into a List
 List<String> list = people.stream().map(Person::getName).collect(Collectors.toList());

 // Accumulate names into a TreeSet
 Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new));

 // Convert elements to strings and concatenate them, separated by commas
 String joined = things.stream()
                       .map(Object::toString)
                       .collect(Collectors.joining(", "));

 // Compute sum of salaries of employee
 int total = employees.stream()
                      .collect(Collectors.summingInt(Employee::getSalary)));

 // Group employees by department
 Map<Department, List<Employee>> byDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment));

 // Compute sum of salaries by department
 Map<Department, Integer> totalByDept
     = employees.stream()
                .collect(Collectors.groupingBy(Employee::getDepartment,
                                               Collectors.summingInt(Employee::getSalary)));

 // Partition students into passing and failing
 Map<Boolean, List<Student>> passingFailing =
     students.stream()
             .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

2
Questo non risponde alla domanda dell'op, inoltre non c'è spiegazione o descrizione del tuo post.
Sid

4

Interfaccia Spliterator: è una funzionalità principale di Stream .

I stream()e parallelStream()di default metodi sono presentati nella Collectioninterfaccia. Questi metodi utilizzano Spliterator attraverso la chiamata a spliterator():

...

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}

...

Spliterator è un iteratore interno che interrompe il flusso nelle parti più piccole. Queste parti più piccole possono essere elaborate in parallelo.

Tra gli altri metodi, ci sono due più importanti per capire Spliterator:

  • boolean tryAdvance(Consumer<? super T> action) A differenza di Iterator, tenta di eseguire l'operazione con l'elemento successivo. Se l'operazione è stata eseguita correttamente, il metodo ritorna true. Altrimenti, ritorna false- ciò significa che c'è assenza di elemento o fine del flusso.

  • Spliterator<T> trySplit() Questo metodo consente di dividere un set di dati in molti set più piccoli in base a uno o l'altro criterio (dimensioni del file, numero di righe, ecc.).


"Se l'operazione è stata eseguita correttamente ..." Probabilmente dovresti riformularla. tryAdvance javadoc è più chiaro: “Se esiste un elemento rimanente, esegue l'azione data su di esso, restituendo vero; altrimenti restituisce false´´
Piro dice
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.