Java Streams para iniciantes: uma introdução ao uso de Streams em Java

Os fluxos Java 8 permitem que os desenvolvedores extraiam dados precisos de uma grande coleção, usando um conjunto de operações predefinidas.

Antes do lançamento do Java 8, o uso do termo "fluxo" em Java seria automaticamente associado ao I / O. No entanto, o Java 8 introduziu um fluxo que pode ser referido como um conjunto de etapas computacionais encadeadas no que é comumente referido como "pipeline de fluxo".

Este artigo irá apresentá-lo aos fluxos Java 8 e demonstrar como eles podem ser úteis em seus projetos.

O que é um fluxo?

Um fluxo é uma interface Java que obtém uma fonte, conduz um conjunto de operações para extrair dados específicos e, em seguida, fornece esses dados ao aplicativo para uso. Essencialmente, ele permite que você extraia dados especializados de uma coleção de dados generalizados.

Como funcionam os streams

Um pipeline de stream sempre começa com uma fonte. O tipo de fonte depende do tipo de dados com os quais você está lidando, mas dois dos mais populares são arrays e coleções.

Para transformar a coleção em um fluxo inicial, você precisará adicionar a função stream () à origem. Isso colocará a origem no pipeline de fluxo, onde várias operações intermediárias diferentes (como filter () e sort () ) podem operar nele.

Depois que todas as operações intermediárias necessárias forem realizadas, você pode introduzir uma operação de terminal (como forEach () ), que produzirá os dados extraídos anteriormente da fonte.

Vida sem córregos

O Java 8 foi lançado em 2014, mas antes disso, os desenvolvedores Java ainda precisavam extrair dados especializados de uma coleção de dados gerais.

Digamos que você tenha uma lista de caracteres aleatórios combinados com números aleatórios para formar valores de string exclusivos, mas deseja apenas os valores que começam com o caractere “C” e deseja organizar o resultado em ordem crescente. É assim que você extrai os dados sem fluxos.

Relacionado: O que você precisa saber sobre como usar strings em Java

Filtrando e classificando valores sem exemplo de fluxos

 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
//declare and initialize the array list
List<String> randomValues = Arrays.asList(
"E11", "D12", "A13", "F14", "C15", "A16",
"B11", "B12", "C13", "B14", "B15", "B16",
"F12", "E13", "C11", "C14", "A15", "C16",
"F11", "C12", "D13", "E14", "D15", "D16"
);
//declare the array list will store needed values
List<String> requiredValues = new ArrayList<>();
//extracting the required values and storing them in reqquiredValues
randomValues.forEach(value -> {
if(value.startsWith("C")) {
requiredValues.add(value);
}
});
//sort the requiredValues in ascending order
requiredValues.sort((String value1, String value2) -> value1.compareTo(value2));
//print each value to the console
requiredValues.forEach((String value) -> System.out.println(value));
}
}

Você também precisará declarar e inicializar a lista de array se estiver usando streams ou algum outro método de extração. O que você não precisaria fazer se estivesse usando streams é declarar uma nova variável para conter os valores exigidos, nem criar as outras cinco linhas de código no exemplo acima.

Relacionado: Como criar e executar operações em matrizes em Java

O código acima produz a seguinte saída no console:

 
C11
C12
C13
C14
C15
C16

Vida com córregos

Na programação, a eficiência fala em produzir o mesmo resultado com significativamente menos código. Isso é exatamente o que um pipeline de fluxo faz para um programador. Então, da próxima vez que alguém perguntar: "por que é importante usar streams em seu projeto?" Simplificando: “streams suportam programação eficiente.”

Continuando com nosso exemplo acima, é assim que a introdução de streams transforma todo o programa.

Filtrando e classificando valores com um exemplo de fluxo

 
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
//declare and initialize the array list
List<String> randomValues = Arrays.asList(
"E11", "D12", "A13", "F14", "C15", "A16",
"B11", "B12", "C13", "B14", "B15", "B16",
"F12", "E13", "C11", "C14", "A15", "C16",
"F11", "C12", "D13", "E14", "D15", "D16"
);
//retrieves only values that start with C, sort them, and print them to the console.
randomValues.stream().filter(value->value.startsWith("C")).sorted().forEach(System.out::println);
}
}

O código acima demonstra o quão poderosa é a interface de fluxo. Ele pega uma lista de valores de array aleatórios e a transforma em um fluxo usando a função stream () . O fluxo é então reduzido a uma lista de array que contém os valores necessários (que são todos os valores que começam com C ), usando a função filter () .

Como você pode ver no exemplo acima, os valores C são organizados aleatoriamente na lista de arrays. Se você fosse imprimir o fluxo neste ponto do pipeline, o valor C15 seria impresso primeiro. Portanto, a função sort () é introduzida no pipeline de fluxo para reorganizar a nova matriz em ordem crescente.

A função final no pipeline do stream é uma função forEach () . Esta é uma função de terminal usada para interromper o pipeline de stream e produz os seguintes resultados no console:

 
C11
C12
C13
C14
C15
C16

Operações intermediárias de fluxo

Há uma extensa lista de operações intermediárias que podem ser usadas em um pipeline de fluxo.

Um pipeline de stream sempre começa com uma única fonte e uma função stream () , e sempre termina com uma única operação de terminal (embora haja várias opções diferentes para escolher). Mas entre essas duas seções está uma lista de seis operações intermediárias você pode usar.

Em nosso exemplo acima, apenas duas dessas operações intermediárias são usadas — filter () e sort () . A operação intermediária que você escolher dependerá das tarefas que deseja realizar.

Se qualquer um dos valores que começam com “C” em nossa lista de arrays acima estivesse em minúsculas e executássemos as mesmas operações intermediárias com eles, obteríamos o seguinte resultado.

Executando operações de filtro e classificação em valores minúsculos, exemplo

 
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
//declare and initialize the array list
List<String> randomValues = Arrays.asList(
"E11", "D12", "A13", "F14", "C15", "A16",
"B11", "B12", "c13", "B14", "B15", "B16",
"F12", "E13", "C11", "C14", "A15", "c16",
"F11", "C12", "D13", "E14", "D15", "D16"
);
//retrieves only values that start with C, sort them, and print them to the console.
randomValues.stream().filter(value->value.startsWith("C")).sorted().forEach(System.out::println);
}
}

O código acima produzirá os seguintes valores no console:

 
C11
C12
C14
C15

O único problema com a saída acima é que ela não representa com precisão todos os valores C em nossa lista de arrays. Uma boa maneira de corrigir esse pequeno erro é apresentar outra operação intermediária ao pipeline de fluxo; esta operação é conhecida como função map () .

Usando o Exemplo de Função de Mapa

 
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
//declare and initialize the array list
List<String> randomValues = Arrays.asList(
"E11", "D12", "A13", "F14", "C15", "A16",
"B11", "B12", "c13", "B14", "B15", "B16",
"F12", "E13", "C11", "C14", "A15", "c16",
"F11", "C12", "D13", "E14", "D15", "D16"
);
//transforms all lower case characters to upper case,
//retrieves only values that start with C, sort them, and print them to the console.
randomValues.stream().map(String::toUpperCase).filter(value->value.startsWith("C")).sorted().forEach(System.out::println);
}
}

A função map () transforma um objeto de um estado para outro; em nosso exemplo acima, ele transforma todos os caracteres minúsculos na lista de array em caracteres maiúsculos.

Colocar a função map () logo antes da função filter () recupera todos os valores que começam com C da lista de arrays.

O código acima produz o seguinte resultado no console, representando com sucesso todos os valores C na lista de arrays.

 
C11
C12
C13
C14
C15
C16

As outras três operações intermediárias que você pode usar em seus aplicativos incluem:

  • olhadinha()
  • limite()
  • pular()

Java 8 Streams Facilita a Criação de Código Eficiente

Com os fluxos Java 8, você pode extrair dados adicionais específicos e relevantes de uma grande fonte com uma linha de código. Contanto que você inclua a função stream () inicial e um operador de terminal, você pode usar qualquer combinação de operações intermediárias que fornecem resultados adequados para seu objetivo.

Se você está se perguntando sobre a linha de código incluída em nossa função filter () ; é conhecido como "expressão lambda". Expressões lambda são outro recurso introduzido com o Java 8 e tem muitos detalhes que podem ser úteis.