Comprensión de Spliterator, Collector y Stream en Java 8

143

Tengo problemas para comprender la Streaminterfaz en Java 8, especialmente cuando tiene que ver con las interfaces Spliteratory Collector. Mi problema es que simplemente no puedo entender Spliteratorlas Collectorinterfaces y todavía, y como resultado, la Streaminterfaz todavía es algo oscura para mí.

¿Qué es exactamente ay Spliteratora Collector, y cómo puedo usarlos? Si estoy dispuesto a escribir el mío Spliteratoro Collector(y probablemente el mío Streamen ese proceso), ¿qué debo hacer y no hacer?

Leí algunos ejemplos dispersos por la web, pero como todo aquí es nuevo y está sujeto a cambios, los ejemplos y tutoriales aún son muy escasos.

Victor Stafusa
fuente

Respuestas:

142

Es casi seguro que nunca tendrá que tratar Spliteratorcomo usuario; solo debería ser necesario si está escribiendo Collectiontipos usted mismo y también tiene la intención de optimizar operaciones paralelas en ellos.

Por lo que vale, una Spliteratores una forma de operar sobre los elementos de una colección de una manera que es fácil dividir parte de la colección, por ejemplo, porque estás paralelizando y quieres que un hilo funcione en una parte de la colección, un hilo para trabajar en otra parte, etc.

Esencialmente Stream, tampoco debería guardar valores de tipo en una variable. Streames algo así como un Iterator, ya que es un objeto de un solo uso que casi siempre usará en una cadena fluida, como en el ejemplo de Javadoc:

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

Collectores la versión más abstracta y generalizada posible de una operación "reduce" a la map / reduce; en particular, necesita soportar pasos de paralelización y finalización. Los ejemplos de Collectors incluyen:

  • sumando, por ejemplo Collectors.reducing(0, (x, y) -> x + y)
  • StringBuilder anexa, p. Ej. Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)
Louis Wasserman
fuente
31
Spliterator (s) también proporciona una forma de transmitir un Iterable que no es una Colección
Bohemian
2
Quise decir "una operación de reducción, en el sentido de que el término se entiende en mapa / reducir"
Louis Wasserman
1
¿ Collectors.ofSe eliminó un método antiguo de versión beta o me falta algo? Para completar, (x,y) -> x+yse puede escribir como Integer::sum.
Jean-François Savard
3
Er, no, lo siento, es Collector.of, no Collectors.of.
Louis Wasserman
2
Su ejemplo de recopilador sería más útil si explicara lo que hace cada uno de sus recopiladores.
MiguelMunoz
90

Spliterator básicamente significa "iterador divisible".

Un solo hilo puede atravesar / procesar todo el Spliterator, pero el Spliterator también tiene un método trySplit()que "dividirá" una sección para que otra persona (típicamente, otro hilo) procese, dejando al spliterator actual con menos trabajo.

Collector combina la especificación de un reduce función (de fama de reducción de mapa), con un valor inicial y una función para combinar dos resultados (permitiendo así que se combinen los resultados de flujos de trabajo divididos).

Por ejemplo, el recopilador más básico tendría un valor inicial de 0, agregaría un número entero a un resultado existente y 'combinaría' dos resultados agregándolos. Sumando así una corriente dividida de enteros.

Ver:

Thomas W
fuente
¿Un valor para combinar dos resultados?
Jason Law
@JasonLaw - aclarado! Gracias por la sugerencia.
Thomas W
5

Los siguientes son ejemplos del uso de los recopiladores predefinidos para realizar tareas comunes de reducción mutable:

 // 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));
Ajay Kumar
fuente
2
Esto no responde la pregunta del Operador, además no hay explicación o descripción de su publicación.
Sid
4

Interfaz Spliterator: es una característica central de Streams .

Los métodos stream()y por parallelStream()defecto se presentan en la Collectioninterfaz. Estos métodos utilizan el Spliterator a través de la llamada a spliterator():

...

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

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

...

Spliterator es un iterador interno que divide la secuencia en las partes más pequeñas. Estas partes más pequeñas se pueden procesar en paralelo.

Entre otros métodos, hay dos más importantes para entender el Spliterator:

  • boolean tryAdvance(Consumer<? super T> action) A diferencia de Iterator, intenta realizar la operación con el siguiente elemento. Si la operación se ejecutó con éxito, el método regresa true. De lo contrario, retornos false, eso significa que hay ausencia de elemento o final de la secuencia.

  • Spliterator<T> trySplit() Este método permite dividir un conjunto de datos en muchos conjuntos más pequeños de acuerdo con uno u otro criterio (tamaño de archivo, número de líneas, etc.).


fuente
"Si la operación se ejecutó con éxito ..." Probablemente debería reformular esto. tryAdvance javadoc es más claro: 'Si existe un elemento restante, realiza la acción dada en él, devolviendo verdadero; más devuelve falso. "
Piro dice Reinstate Monica