Introdução ao Java 8 Streams
1. Visão geral
Neste artigo, daremos uma olhada rápida em uma das principais peças da nova funcionalidade que o Java 8 adicionou - Streams.
Explicaremos sobre o que são os fluxos e mostraremos a criação e as operações básicas de fluxo com exemplos simples.
2. API Stream
Um dos principais novos recursos do Java 8 é a introdução da funcionalidade de fluxo -java.util.stream - que contém classes para processar sequências de elementos.
A classe de API central éStream<T>.. A seção a seguir demonstrará como os fluxos podem ser criados usando as fontes de provedor de dados existentes.
2.1. Criação de fluxo
Os fluxos podem ser criados a partir de diferentes fontes de elementos, por exemplo coleção ou matriz com a ajuda dos métodosstream()eof():
String[] arr = new String[]{"a", "b", "c"};
Stream stream = Arrays.stream(arr);
stream = Stream.of("a", "b", "c");
Um método padrãostream() é adicionado à interfaceCollection e permite criar umStream<T> usando qualquer coleção como uma fonte de elemento:
Stream stream = list.stream();
2.2. Multi-threading com Streams
A API Stream também simplifica o multithreading, fornecendo o métodoparallelStream() que executa operações sobre os elementos do stream em modo paralelo
O código a seguir permite executar o métododoWork() em paralelo para cada elemento do fluxo:
list.parallelStream().forEach(element -> doWork(element));
Na seção a seguir, apresentaremos algumas das operações básicas da API de Stream.
3. Operações de fluxo
Existem muitas operações úteis que podem ser executadas em um fluxo.
Eles são divididos emintermediate operations (retornaStream<T>) eterminal operations (retorna um resultado de tipo definido). Operações intermediárias permitem encadeamento.
Também é importante notar que as operações em streams não mudam a fonte.
Aqui está um exemplo rápido:
long count = list.stream().distinct().count();
Portanto, o métododistinct() representa uma operação intermediária, que cria um novo fluxo de elementos exclusivos do fluxo anterior. E o métodocount() é uma operação de terminal, que retorna o tamanho do fluxo.
3.1. Iterando
A API de fluxo ajuda a substituir os loopsfor, for-each ewhile. Permite concentrar-se na lógica de operação, mas não na iteração sobre a sequência de elementos. Por exemplo:
for (String string : list) {
if (string.contains("a")) {
return true;
}
}
Este código pode ser alterado apenas com uma linha do código Java 8:
boolean isExist = list.stream().anyMatch(element -> element.contains("a"));
3.2. Filtragem
O métodofilter() nos permite escolher um fluxo de elementos que satisfazem um predicado.
Por exemplo, considere a seguinte lista:
ArrayList list = new ArrayList<>();
list.add("One");
list.add("OneAndOnly");
list.add("Derek");
list.add("Change");
list.add("factory");
list.add("justBefore");
list.add("Italy");
list.add("Italy");
list.add("Thursday");
list.add("");
list.add("");
O código a seguir cria umStream<String> deList<String>, encontra todos os elementos deste fluxo que contêmchar “d”e cria um novo fluxo contendo apenas os elementos filtrados:
Stream stream = list.stream().filter(element -> element.contains("d"));
3.3. Mapeamento
Para converter elementos de aStream aplicando uma função especial a eles e para coletar esses novos elementos emStream, podemos usar o métodomap():
List uris = new ArrayList<>();
uris.add("C:\\My.txt");
Stream stream = uris.stream().map(uri -> Paths.get(uri));
Portanto, o código acima converteStream<String> emStream<Path> aplicando uma expressão lambda específica a cada elemento doStream inicial.
Se você tem um fluxo em que cada elemento contém sua própria sequência de elementos e deseja criar um fluxo desses elementos internos, deve usar o métodoflatMap():
List details = new ArrayList<>();
details.add(new Detail());
Stream stream
= details.stream().flatMap(detail -> detail.getParts().stream());
Neste exemplo, temos uma lista de elementos do tipoDetail. A classeDetail contém um campoPARTS, que é aList<String>. Com a ajuda deflatMap() método cada elemento do campoPARTS será extraído e adicionado ao novo fluxo resultante. Depois disso, oStream<Detail> inicial será perdido.
3.4. Coincidindo
A API de fluxo fornece um conjunto prático de instrumentos para validar elementos de uma sequência de acordo com algum predicado. Para fazer isso, um dos seguintes métodos pode ser usado:anyMatch(), allMatch(), noneMatch(). Seus nomes são autoexplicativos. Essas são operações de terminal que retornam um valor booleano.
boolean isValid = list.stream().anyMatch(element -> element.contains("h")); // true
boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); // false
boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); // false
3.5. Redução
A API Stream permite reduzir uma sequência de elementos a algum valor de acordo com uma função especificada com a ajuda do métodoreduce() do tipoStream. Este método usa dois parâmetros: primeiro - valor inicial, segundo - uma função acumuladora.
Imagine que você tem umList<Integer> e deseja ter uma soma de todos esses elementos e algunsInteger iniciais (neste exemplo 23). Portanto, você pode executar o código a seguir e o resultado será 26 (23 + 1 + 1 + 1).
List integers = Arrays.asList(1, 1, 1);
Integer reduced = integers.stream().reduce(23, (a, b) -> a + b);
3.6. Coletando
A redução também pode ser fornecida pelo métodocollect() do tipoStream. Esta operação é muito útil no caso de converter um fluxo paraCollection ouMape representar um fluxo na forma de uma única string. Há uma classe de utilidadeCollectors que fornece uma solução para quase todas as operações de coleta típicas. Para algumas tarefas não triviais, umCollector personalizado pode ser criado.
List resultList
= list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());
Este código usa a operação de terminalcollect() para reduzir aStream<String> paraList<String>.
4. Conclusões
Neste artigo, abordamos brevemente os fluxos Java - definitivamente um dos recursos mais interessantes do Java 8.
Existem exemplos muito mais avançados de uso do Streams; o objetivo deste artigo era apenas fornecer uma introdução rápida e prática ao que você pode começar a fazer com a funcionalidade e como ponto de partida para explorar e aprender mais.
O código-fonte que acompanha o artigo está disponívelover on GitHub.