Introdução ao StreamEx
1. Visão geral
Um dos recursos mais interessantes do Java 8 é o link:/java-8-streams [Stream API] - que, simplesmente, é uma ferramenta poderosa para processar sequências de elementos.
_https://amaembo.github.io/streamex/javadoc/[StreamEx] _ é uma biblioteca que fornece funcionalidade adicional para a API Stream padrão, juntamente com as melhorias de desempenho.
Aqui estão alguns recursos principais:
-
Maneiras mais curtas e convenientes de executar as tarefas diárias
-
100% de compatibilidade com o JDK original Streams
-
Simpatia para processamento paralelo: qualquer novo recurso aproveita ao máximo os fluxos paralelos *Desempenho e sobrecarga mínima. Se StreamEx permitir resolver a tarefa usando menos código em comparação com o _Stream padrão, _ não deve ser significativamente mais lento que o normal (e às vezes é ainda mais rápido)
Neste tutorial, apresentaremos alguns dos recursos da API StreamEx.
===* 2. Configurando o exemplo *
Para usar StreamEx, precisamos adicionar a seguinte dependência ao pom.xml:
<dependency>
<groupId>one.util</groupId>
<artifactId>streamex</artifactId>
<version>0.6.5</version>
</dependency>
A versão mais recente da biblioteca pode ser encontrada em Maven Central.
Neste tutorial, vamos usar uma classe User simples:
public class User {
int id;
String name;
Role role = new Role();
//standard getters, setters, and constructors
}
E uma simples classe Role:
public class Role {
}
===* 3. Métodos de atalho dos coletores *
Uma das operações de terminal mais populares do Streams é a operação collect; isso permite reembalar elementos Stream para uma coleção de nossa escolha.
O problema é que o código pode ficar desnecessariamente detalhado para cenários simples:
users.stream()
.map(User::getName)
.collect(Collectors.toList());
3.1. Coletando em uma coleção
Agora, com o StreamEx, não precisamos fornecer um Collector para especificar que precisamos de List, _Set, Map, InmutableList, _ etc .:
List<String> userNames = StreamEx.of(users)
.map(User::getName)
.toList();
*A operação _collect_ ainda está disponível na API se queremos executar algo mais complicado do que pegar elementos de um _Stream_ e colocá-los em uma coleção.*
3.2. Coletores avançados
Outra abreviação é groupingBy:
Map<Role, List<User>> role2users = StreamEx.of(users)
.groupingBy(User::getRole);
Isso produzirá um Map com o tipo de chave especificado na referência do método, produzindo algo semelhante ao grupo pela operação no SQL.
Usando a API Stream simples, precisaríamos escrever:
Map<Role, List<User>> role2users = users.stream()
.collect(Collectors.groupingBy(User::getRole));
Um formulário abreviado semelhante pode ser encontrado para _Collectors.joining (): _
StreamEx.of(1, 2, 3)
.joining("; ");//"1; 2; 3"
O que leva todos os elementos no Stream a produz um String concatenando todos eles.
*4. Adicionando, removendo e selecionando elementos *
Em alguns cenários,* temos uma lista de objetos de tipos diferentes e precisamos filtrá-los por tipo: *
List usersAndRoles = Arrays.asList(new User(), new Role());
List<Role> roles = StreamEx.of(usersAndRoles)
.select(Role.class)
.toList();
*Podemos adicionar elementos ao início ou ao final do nosso* _ *Stream* , _ com estas operações úteis:
List<String> appendedUsers = StreamEx.of(users)
.map(User::getName)
.prepend("(none)")
.append("LAST")
.toList();
*Podemos remover elementos nulos indesejados usando _nonNull () _* e usar o _Stream_ como um _Iterable_:
for (String line : StreamEx.of(users).map(User::getName).nonNull()) {
System.out.println(line);
}
*5. Suporte a operações matemáticas e tipos primitivos *
StreamEx adiciona suporte para tipos primitivos, como podemos ver neste exemplo autoexplicativo:
short[] src = {1,2,3};
char[] output = IntStreamEx.of(src)
.map(x -> x* 5)
.toCharArray();
Agora vamos dar uma matriz de elementos double de maneira desordenada. Queremos criar uma matriz que consiste na diferença entre cada par.
Podemos usar o método pairMap para executar esta operação:
public double[] getDiffBetweenPairs(double... numbers) {
return DoubleStreamEx.of(numbers)
.pairMap((a, b) -> b - a)
.toArray();
}
6. Operações do mapa
6.1. Filtrando por Chaves
Outro recurso útil é a capacidade de criar um Stream a partir de um Map e filtrar os elementos usando os valores para os quais eles apontam.
Nesse caso, estamos pegando todos os valores não nulos:
Map<String, Role> nameToRole = new HashMap<>();
nameToRole.put("first", new Role());
nameToRole.put("second", null);
Set<String> nonNullRoles = StreamEx.ofKeys(nameToRole, Objects::nonNull)
.toSet();
6.2. Operando em pares de valor-chave
Também podemos operar em pares de valores-chave criando uma instância EntryStream:
public Map<User, List<Role>> transformMap(
Map<Role, List<User>> role2users) {
Map<User, List<Role>> users2roles = EntryStream.of(role2users)
.flatMapValues(List::stream)
.invert()
.grouping();
return users2roles;
}
A operação especial EntryStream.of pega um Map e o transforma em um Stream de objetos de valor-chave. Em seguida, usamos a operação flatMapValues para transformar nossa lista de funções em um Stream de valores únicos.
Em seguida, podemos inverter o par chave-valor, transformando a classe User na chave e a classe Role no valor.
E, finalmente, podemos usar a operação grouping para transformar nosso mapa na inversão do que foi recebido, todos com apenas quatro operações.
6.3. Mapeamento de valor-chave
Também podemos mapear chaves e valores independentemente:
Map<String, String> mapToString = EntryStream.of(users2roles)
.mapKeys(String::valueOf)
.mapValues(String::valueOf)
.toMap();
Com isso, podemos transformar rapidamente nossas chaves ou valores em outro tipo necessário.
*7. Operações de arquivo *
Usando StreamEx, podemos ler arquivos com eficiência, ou seja, sem carregar arquivos completos de uma só vez. É útil ao processar arquivos grandes:
StreamEx.ofLines(reader)
.remove(String::isEmpty)
.forEach(System.out::println);
Observe que usamos o método _remove () _ para filtrar as linhas vazias.
O ponto a ser observado aqui é que o StreamEx não fechará o arquivo automaticamente. Portanto, devemos lembrar de executar manualmente a operação de fechamento na ocasião da leitura e gravação de arquivos, para evitar sobrecarga desnecessária de memória.
===* 8. Conclusão*
Neste tutorial, aprendemos sobre o StreamEx e seus diferentes utilitários. Há muito mais a ser percorrido - e eles têm uma útil citação aqui.
Como sempre, o código fonte completo está disponível over no GitHub.