Noções básicas sobre Spliterator, Collector e Stream em Java 8

143

Estou tendo problemas para entender a Streaminterface no Java 8, especialmente no que diz respeito às interfaces Spliteratore Collector. Meu problema é que eu simplesmente não consigo entender Spliteratore as Collectorinterfaces ainda e, como resultado, a Streaminterface ainda é um tanto obscura para mim.

O que exatamente é a Spliteratore a Collector, e como posso usá-los? Se estou disposto a escrever o meu próprio ( Spliteratorou Collectorprovavelmente o meu Streamnesse processo), o que devo fazer e não fazer?

Li alguns exemplos espalhados pela web, mas como tudo aqui ainda é novo e está sujeito a alterações, exemplos e tutoriais ainda são muito escassos.

Victor Stafusa
fonte

Respostas:

142

Você quase certamente nunca terá que lidar Spliteratorcomo usuário; ele só deve ser necessário se você estiver escrevendo Collectiontipos si mesmo e também com a intenção de otimizar parallelized operações sobre eles.

Pelo que vale, a Spliteratoré uma maneira de operar sobre os elementos de uma coleção de uma maneira que é fácil separar parte da coleção, por exemplo, porque você está paralelizando e deseja que um encadeamento funcione em uma parte da coleção, um segmento para trabalhar em outra parte, etc.

Você também nunca deve salvar valores do tipo Streamem uma variável. Streamé como uma espécie Iterator, pois é um objeto de uso único que você quase sempre usa em uma cadeia fluente, como no exemplo do Javadoc:

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

Collectoré a versão mais generalizada e abstrata possível de uma operação "reduzir" a map / red; em particular, ele precisa dar suporte às etapas de paralelização e finalização. Exemplos de Collectors incluem:

  • soma, por exemplo Collectors.reducing(0, (x, y) -> x + y)
  • StringBuilder anexando, por exemplo Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString)
Louis Wasserman
fonte
31
O Spliterator (s) também fornece uma maneira de transmitir um Iterable que não é uma coleção
Bohemian
2
Eu quis dizer "a reduzir a operação, no sentido de que prazo se entende no mapa / reduzir"
Louis Wasserman
1
É Collectors.ofum método antigo da versão beta que foi removido ou estou-me faltando alguma coisa? Para completar, (x,y) -> x+ypode ser escrito como Integer::sum.
Jean-François Savard
3
Er, não, desculpe, é Collector.of, não Collectors.of.
Louis Wasserman
2
Seu exemplo de coletor seria mais útil se você explicasse o que cada um de seus coletores faz.
MiguelMunoz
90

Spliterator basicamente significa "Iterador divisível".

Um thread único pode percorrer / processar todo o Spliterator em si, mas o Spliterator também possui um método trySplit()que " separará " uma seção para outra pessoa (normalmente, outro thread) processar - deixando o atual spliterator com menos trabalho.

Collectorcombina a especificação de uma reducefunção (de fama de redução de mapa), com um valor inicial e uma função para combinar dois resultados (permitindo assim que os resultados dos fluxos de trabalho Spliterated sejam combinados).

Por exemplo, o coletor mais básico teria um valor inicial de 0, adicionaria um número inteiro a um resultado existente e 'combinaria' dois resultados adicionando-os. Somando assim um fluxo dividido de números inteiros.

Vejo:

Thomas W
fonte
um valor para combinar dois resultados?
Jason Law
@JasonLaw - esclarecido! Obrigado pela sugestão.
Thomas W
5

A seguir, exemplos de uso de coletores predefinidos para executar tarefas comuns de redução mutável:

 // 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
fonte
2
Isso não responde à pergunta do Op, além de não haver explicação ou descrição da sua postagem.
Sid
4

Interface Spliterator- é um recurso principal do Streams .

Os métodos stream()e parallelStream()padrão são apresentados na Collectioninterface. Esses métodos usam o Spliterator através da chamada para spliterator():

...

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

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

...

Spliterator é um iterador interno que divide o fluxo em partes menores. Essas peças menores podem ser processadas em paralelo.

Entre outros métodos, existem dois mais importantes para entender o Spliterator:

  • boolean tryAdvance(Consumer<? super T> action) Ao contrário do Iterator, ele tenta executar a operação com o próximo elemento. Se a operação for executada com sucesso, o método retornará true. Caso contrário, retorna false- isso significa que há ausência de elemento ou final do fluxo.

  • Spliterator<T> trySplit() Este método permite dividir um conjunto de dados em muitos conjuntos menores, de acordo com um ou outro critério (tamanho do arquivo, número de linhas, etc.).


fonte
'Se a operação for executada com sucesso ...' Você provavelmente deve reformular isso. O tryAdvance javadoc é mais claro: 'Se um elemento restante existir, execute a ação especificada, retornando true; else retorna false.´
Piro diz Reinstate Monica