0

Resumo Collections e Streams com Java

Vinicius Almeida
Vinicius Almeida

Olá pessoal, como de costume trago mais um resumo pra vocês. Caso encontrem algum erro, me avisem para que eu possa corrigir. Se o artigo foi útil para você relembrar os conceitos abordados no módulo, deixe seu comentário.

Implementando Collections e Streams com Java

Lists

  • As listas garantem a ordem de inserção, permitem adição, atualização, leitura e remoção dos itens sem regras adicionais. Também permite a ordenação através de comparadores.
  • Nesta parte do módulo aprendemos a utilizar as seguintes bibliotecas: java.util.ArrayList ; java.util.List ; java.util.Collections e java.util.Iterator.

Criando um ArrayList e um Vector

  • Para criar uma ArrayList, precisamos instanciar da seguinte forma:
ArrayList:
List<'tipo'> 'nomeLista' = new ArrayList<>();
Exemplo:
List<String> nomes = new ArrayList<>();
​
​
Vector:
List<'tipo'> 'nomeVector' = new Vector<>();
Exemplo:
List<String> esportes = new Vector<>();

Métodos Utilizados

  • *Estes métodos funcionam tanto para ArrayList ou Vector.
  • add()
  • Podemos adicionar itens em nossa lista através do método add(), onde o tipo de dado passado dentro do add deve ser do mesmo tipo da lista criada.
  • Exemplo:
nomes.add("Carlos");
  • set()
  • O método set() nos permite alterar o valor de uma determinada posição dentro da lista.
  • Exemplo:
lista.set('posição', 'novoValor');
Exemplo:
nomes.set(2, "Larissa");
*Altera o valor do elemento na posição 2 da lista nomes para Larissa
  • remove()
  • O método remove serve para remover um item da lista e possui dois construtores, onde um deles remove um elemento em uma determinada posição e o outro remove um determinado elemento passado como parâmetro caso ele seja igual à um elemento que exista na lista.
  • Exemplo:
Remove o elemento na posição 4 da lista nomes:
nomes.remove(4)
​
Remove o elemento Wesley da lsita nomes:
nomes.remove("Wesley");
  • get()
  • O método get() pega o valor do elemento em uma determinada posição.
  • Exemplo:
Pega o valor do elemento que está na posição 3 da lista nomes
e atribui seu valor a variável nome:
​
String nome = nomes.get(3);
  • indexOf()
  • Retorna um inteiro representando a posição do elemento passado nos parâmetros. Caso não possua o elemento passado nos parâmetros, o método retornará o valor de -1.
  • Exemplo:
Retorna a posição do item Wesley e atribui 
seu index na variável posicao:
​
int posicao = nomes.indexOf("Wesley");
  • size()
  • Retorna um inteiro que representa o número de posições existentes dentro da lista ou, simplesmente o tamanho da lista.
  • Exemplo:
Retorna o numero de posições alocadas dentro da lista nomes e salva seu valor dentro da variável tamanho:
​
int tamanho = nomes.size();
  • contains()
  • Este método retorna um booleano. Caso contenha na lista o elemento passado no parâmetro, o método retornará true, caso o contrário, retornará false.
  • Exemplo:
Verifica se existe o valor Fernando dentro da lista e atribui esta verificação como um booleano dentro da variável temFernando:
​
boolean temFernando = nomes.contains("Fernando");
  • isEmpty()
  • Este método verifica se a lista que está sendo manipulada, está vazia e retorna um booleano, anunciando seu estado. Caso a lista contenha elementos, este método retornará true, caso o contrário, retornará false.
  • Exemplo:
Verifica se a lista nomes está vazia e atribui seu estado dentro da variável listaEstaVazia:
​
boolean listaEstaVazia = nomes.isEmpty();
  • clear()
  • O método clear remove todos os itens da lista manipulada.
  • Exemplo:
Remove todos os elementos da lista nomes:
​
nomes.clear();
  • Collections.sort()
  • Este método da biblioteca java.util.Collections nos permite fazer a ordenação de dados simples como Strings ou Inteiros. Como em nossos exemplos estamos utilizando apenas Strings, este método nos serve bem.
  • Exemplo:
Ordenada Alfabeticamente a lista nomes:
​
Collections.sort(nomes);
  • Iterando Listas
  • Aprendemos a iterar listas de duas formas nas aulas:
  • Com laço de repetição for-each
  • Percorre cada elemento da lista e executando uma ação.
  • Exemplo do corpo de um for-each :
for (tipoVariavel nomeVariavel : nomeArray/Lista) {
 //Código a ser executado.
}
​
//*Onde o 'nomeVariavel' pode ser qualquer nome que quiser
  • Exemplo:
Pega a variavel nomeDoItem do Tipo String da lista nomes e imprime uma string concatenada com a variável 'nomeDoItem' :
​
for (String nomeDoItem : nomes) {
​
 System.out.println("-->" + nomeDoItem);
​
}
  • Utlizando o método Iterator
  • Este método retorna um objeto do tipo iterator. Sua manipulação se da através do laço de repetição While. Para isso se faz necessário explicar dois métodos do iterator:
  • Método hasNext()
  • Este método retorna true caso a lista possua mais elementos e false caso a lista não tenha mais elementos.
  • Método next()
  • O método next() retorna o próximo elemento contido na lista.
  • Exemplo:
Pega o iterator da lista nomes e atribui seu valor na variavel iterator:
​
Iterator<String> iterator = nomes.iterator();
​
O laço while checa se existe o próximo elemento do iterator. Caso exista, o programa imprime uma string concatenada com o próximo valor do iterator através do método next() :
​
while (iterator.hasNext()) {
​
 System.out.println("---->" + iterator.next());
​
}

Queue

  • A interface queue é muito utilizada para a implementação de filas, onde o primeiro elemento que entra na fila, deve ser o primeiro elemento a sair. A interface queue garante a ordem de inserção, além de permite a adição, leitura e remoção do elemento, porém não permite a ordenação da lista.
  • Para utilizar a interface queue, precisamos importar as bibliotecas java.util.LinkedList e java.util.Queue.

Declarando um Queue

Queue<tipo> nomeQueue = new LinkedList<>();
​
Exemplo:
Queue<String> filaBanco = new LinkedList<>();

Métodos utilizados

  • add()
  • Adiciona um elemento a fila.
  • Exemplo:
filaBanco.add("Pedro");
filaBanco.add("Edson");
filaBanco.add("João");
filaBanco.add("Jonas");
  • peek()
  • Retorna o primeiro elemento da lista mas não o remove.
  • Exemplo:
Pega o primeiro elemento da lista e armazena 
na variavel primeiroNaFila:
​
String primeiroNaFila = filaBanco.peek();
  • poll()
  • Retorna o primeiro elemento da lista e então remove este elemento.
  • Exemplo:
Pega o primeiro elemento da fila filaBanco, armazena este elemento na variável primeiroNaFilaRemovido e remove este elemento da fila:
​
String primeiroNaFilaRemovido = filaBanco.poll();
  • size()
  • Retorna um inteiro com o tamanho da fila.
  • Exemplo:
System.out.println(filaBanco.size());
  • element()
  • Muito semelhante ao método peek(), retorna o primeiro elemento da lista mas não o remove. Sua divergência com o método peek() está no fato de element() lançar uma exceção caso a fila esteja vazia, o que o método peek() não faz.
  • Exemplo:
String primeiroClienteOuErro = filaBanco.element();
  • clear()
  • Remove todos os elementos da fila.
  • Exemplo:
filaBanco.clear();
  • isEmpty()
  • Retorna um booleano True se a fila estiver vazia ou False se a fila conter algum elemento.
  • Exemplo:
System.out.println(filaBanco.isEmpty());
  • Iterando filas
  • Podemos iterar filas do mesmo jeito que aprendemos a iterar listas. Através do for-each e da interface Iterator.
  • Exemplo com for-each:
for (String proximoNaFila: filaBanco) {
 System.out.println(proximoNaFila);
}
  • Exemplo com Iterator:
Iterator<String> iterator = filaBanco.iterator();
​
while (iterator.hasNext()) {
  System.out.println(iterator.next());
}

Set

  • A interface set faz parte da biblioteca java.util.Set e foi criada para ser performática. Por padrão, ela não garante ordem de inserção, não aceita itens repetidos e não aceita ordenação. Esta interface permite adição e remoção de itens normalmente, porém como a interface set não possui índice, buscas por elementos específicos(índices) não são possíveis. Dentro da interface set, podemos utilizar as implementações : HashSet, LinkedHashSet e TreeSet.


  • HashSet
  • Como este set não é ordenado e não permite valores repetidos, costuma ser utilizado quando não é necessário manter uma ordenação. Por não ter repetição de valores e não ser ordenado, é a implementação mais performática para leitura. Utiliza a interface java.util.HashSet.
  • Declaração de um HashSet
Set<Double> notasAlunos = new HashSet<>();
  • Métodos
  • Possui os métodos : add(), remove(), size(), clear(), isEmpty(), entre outros.
  • Iteração
  • Pode ser iterado através do for-each ou da Classe Iterator.
  • Códigos Utilizados na aula:
Set<Double> notasAlunos = new HashSet<>();
​
   // Adiciona as notas no set
   notasAlunos.add(5.8);
   notasAlunos.add(9.3);
   notasAlunos.add(6.5);
   notasAlunos.add(10.0);
   notasAlunos.add(5.4);
   notasAlunos.add(7.3);
   notasAlunos.add(3.8);
   notasAlunos.add(4.0);
​
   System.out.println(notasAlunos);
​
   // Remove a nota do set
   notasAlunos.remove(3.8);
​
   System.out.println(notasAlunos);
​
   //Retorna a quantidade de itens do set
   System.out.println(notasAlunos.size());
​
   // Navega em todos os itens do iterator
   Iterator<Double> iterator = notasAlunos.iterator();
​
   while (iterator.hasNext()) {
     System.out.println(iterator.next());
   }
​
   for (Double nota: notasAlunos) {
     System.out.println(nota);
   }
​
//   notasAlunos.clear();
​
   // Retorna se o set está vazio ou não
   System.out.println(notasAlunos.isEmpty());
​
 }

LinkedHashSet

  • O LinkedHashSet mantém a ordem de inserção dos elementos. É utilizado quando é necessário manter a ordem de inserção dos elementos e por isso, é a implementação mais lenta. Utiliza a interface java.util.LinkedHashSet.
  • Declaração de um LinkedHashSet
LinkedHashSet<Integer> sequenciaNumerica = new LinkedHashSet<>();
  • Métodos
  • Possui os métodos : add(), remove(), size(), clear(), isEmpty(), entre outros.
  • Iteração
  • Pode ser iterado através do for-each ou da Classe Iterator.
  • Códigos Utilizados na aula:
LinkedHashSet<Integer> sequenciaNumerica = new LinkedHashSet<>();
​
   // Adiciona os numeros no set
   sequenciaNumerica.add(16);
   sequenciaNumerica.add(2);
   sequenciaNumerica.add(8);
   sequenciaNumerica.add(4);
   sequenciaNumerica.add(1);
​
   System.out.println(sequenciaNumerica);
​
   // Remove o número do set
   sequenciaNumerica.remove(4);
​
   System.out.println(sequenciaNumerica);
​
   //Retorna a quantidade de itens do set
   System.out.println(sequenciaNumerica.size());
​
   // Navega em todos os itens do iterator
   Iterator<Integer> iterator = sequenciaNumerica.iterator();
​
   while (iterator.hasNext()) {
     System.out.println(iterator.next());
   }
​
   for (Integer numero: sequenciaNumerica) {
     System.out.println(numero);
   }
​
   sequenciaNumerica.clear();
​
   // Retorna se o set está vazio ou não
   System.out.println(sequenciaNumerica.isEmpty());
​
 }

TreeSet

  • Este set mantém a ordem e pode ser reordenado através de comparators. Sua utilização se da quando é necessário alterar sua ordem.
  • O TreeSet é performático para leitura. Para modificação tem a necessidade de reordenar, sendo mais lento que o LinkedHashSet.
  • Utiliza a interface java.util.TreeSet.
  • *Note que sempre que um elemento for adicionado, a ordem dos elementos poderá mudar.
  • Declaração de um TreeSet
TreeSet<String> treeCapitais = new TreeSet<>();
  • Métodos
  • Além dos métodos comuns de Collections , temos os seguintes métodos:
  • first()
  • Retorna o elemento que está no topo da arvore.
  • last()
  • Retorna o elemento que está no final da arvore.
  • lower(obj)
  • Retorna o elemento que está abaixo do elemento obj.
  • higher(obj)
  • Retorna o elemento que está acima do elemento obj.
  • pollFirst()
  • Retorna o elemento no topo da árvore e o remove do set.
  • pollLast()
  • Retorna o elemento no final da árvore e o remove do set.
  • Iteração
  • Pode ser iterado através do for-each ou da Classe Iterator.
  • Códigos Utilizados na aula:
TreeSet<String> treeCapitais = new TreeSet<>();
​
   // Monta a árvore com as capitais
   treeCapitais.add("Porto Alegre");
   treeCapitais.add("Florianópolis");
   treeCapitais.add("Curitiba");
   treeCapitais.add("São Paulo");
   treeCapitais.add("Rio de Janeiro");
   treeCapitais.add("Belo Horizonte");
​
   System.out.println(treeCapitais);
​
   // Retorna a primeira capital no topo da árvore
   System.out.println(treeCapitais.first());
​
   // Retorna a última capital no final da árvore
   System.out.println(treeCapitais.last());
​
   // Retorna a primeira capital abaixo na árvore da capital parametrizada
   System.out.println(treeCapitais.lower("Florianópolis"));
​
   // Retorna a primeira capital acima na árvore da capital parametrizada
   System.out.println(treeCapitais.higher("Florianópolis"));
​
   // Exibe todas as capitais no console
   System.out.println(treeCapitais);
​
   // Retorna a primeira capital no topo da árvore, removendo do set
   System.out.println(treeCapitais.pollFirst());
​
   // Retorna a primeira capital no final da árvore, removendo do set
   System.out.println(treeCapitais.pollLast());
​
   // Exibe todas as capitais no console
   System.out.println(treeCapitais);
​
   // Navega em todos os itens do iterator
   Iterator<String> iterator = treeCapitais.iterator();
​
   while (iterator.hasNext()) {
     System.out.println(iterator.next());
   }
​
   for (String capital: treeCapitais) {
     System.out.println(capital);
   }
​
 }

Map

  • A primeira coisa que precisamos entender sobre a interface Map é que ela não extende de Collections como vimos para as outras interfaces abordadas. A interface Map armazena dois objetos sendo eles chave e valor. Permite valores repetidos mas não permite repetição de chave. Permite adição, busca por chave ou valor, atualização, remoção e navegação, além de poder ser ordenado. Dentro de Map, utilizamos as interfaces java.util.HashMap, java.util.TreeMap e java.util.HashTable.
  • HashMap
  • É o Map mais comum.
  • Declarando um HashMap
Map<tipoChave, tipoValor> nomeMap = new HashMap<>();
​
Exemplo:
Map<String, Integer> campeoesMundialFifa = new HashMap<>();
  • Métodos
  • put(chave, valor)
  • Adiciona uma chave e valor ao Map. Como o Map não aceita chaves repetidas, caso a chave já exista no Map , seu valor será atualizado.
  • Exemplo:
Adiciona ao HashMap campeoesMundialFifa a chave "Brasil" e 
o valor 5:
​
campeoesMundialFifa.put("Brasil", 5);
  • get(chave)
  • Pega o valor associado a chave passa no parâmetro.
  • Exemplo:
Pega o valor associado a chave Argentina:
​
campeoesMundialFifa.get("Argentina");
  • containsKey(chave)
  • Retorna um booleano True, se a chave existir dentro do Map ou false caso ela não exista.
  • Exemplo:
campeoesMundialFifa.containsKey("França");
  • remove(chave)
  • Remove o elemento do Map com base em sua chave se ele estiver presente.
  • Exemplo:
campeoesMundialFifa.remove("França");
  • containsValue(valor)
  • Retorna um booleano True caso o Map contenha o valor passado no parâmetro ou false caso o contrário.
  • Exemplo:
System.out.println(campeoesMundialFifa.containsValue(6));
  • size()
  • Retorna um inteiro com o número de elementos chave-valor no Map.
  • Exemplo:
System.out.println(campeoesMundialFifa.size());
  • Iterando um HashMap
// Navega nos registros do mapa
Pedimos o entrySet(), o qual retorna um set na variável entry.
E por esta variável, conseguimos pegar a chave(getKey()) e o valor(getValue()).
​
for(Map.Entry<String, Integer> entry :campeoesMundialFifa.entrySet()) {
 System.out.println(entry.getKey() + " -- " + entry.getValue());
}
​
// Navega nos registros do mapa
for (String key : campeoesMundialFifa.keySet()) 
{
 System.out.println(key + " -- " + campeoesMundialFifa.get(key));
}

TreeMap

  • Declarando um TreeMap
TreeMap<tipoChave, tipoValor> nomeTreeMap = new TreeMap<>();
​
Exemplo:
TreeMap<String, String> treeCapitais = new TreeMap<>();
  • Métodos
  • put(chave, valor)
  • Adiciona uma chave e valor ao Map. Como o Map não aceita chaves repetidas, caso a chave já exista no Map , seu valor será atualizado.
  • Exemplo:
Adiciona ao TreeMap treeCapitais a chave "RS" e 
o valor "Porto Alegre":
​
treeCapitais.put("RS", "Porto Alegre");
  • firstKey()
  • Retorna o primeiro valor da TreeMap.
  • Exemplo:
System.out.println(treeCapitais.firstKey());
  • lastKey()
  • Retorna o último valor da TreeMap.
  • Exemplo:
System.out.println(treeCapitais.lastKey());
  • lowerKey(chave)
  • Retorna a chave que está abaixo da chave passada no parâmetro do método.
  • Exemplo:
System.out.println(treeCapitais.lowerKey("SC"));
  • higherKey(chave)
  • Retorna a chave que está acima da chave passada no parâmetro do método.
  • Exemplo:
System.out.println(treeCapitais.higherKey("SC"));
  • firstEntry()
  • Retorna a primeira chave-valor da TreeMap.
  • Exemplo:
// Retorna a primeira capital no topo da árvore
System.out.println(treeCapitais.firstEntry().getKey() + " - " 
+ treeCapitais.firstEntry().getValue());
  • lastEntry()
  • Retorna a última chave-valor da TreeMap.
  • Exemplo:
// Retorna a última capital no final da árvore
​
System.out.println(treeCapitais.lastEntry().getKey() + " - " 
+ treeCapitais.lastEntry().getValue());
  • lowerEntry(chave)
  • Retorna a chave-valor que está abaixo da chave passada por parâmetro no método.
  • Exemplo:
// Retorna a primeira capital abaixo na árvore da capital parametrizada
​
System.out.println(treeCapitais.lowerEntry("SC").getKey() + " - " + treeCapitais.lowerEntry("SC").getValue());
  • higherEntry(chave)
  • Retorna a chave-valor que está acima da chave passada por parâmetro no método.
  • Exemplo:
 // Retorna a primeira capital acima na árvore da capital parametrizada
   
System.out.println(treeCapitais.higherEntry("SC").getKey() +" - " + treeCapitais.higherEntry("SC").getValue());
​
  • pollFirstEntry()
  • Retorna a primeira chave-valor do TreeMap e a remove.
  • Exemplo:
Map.Entry<String, String> firstEntry = treeCapitais.pollFirstEntry();
​
// Retorna a primeira capital no topo da árvore, removendo do map
System.out.println(firstEntry.getKey() + " - " + firstEntry.getValue());
  • pollLastEntry()
  • Retorna a última chave-valor do TreeMap e a remove.
  • Exemplo:
Map.Entry<String, String> lastEntry = treeCapitais.pollLastEntry();
​
// Retorna a primeira capital no final da árvore, removendo do map
System.out.println(lastEntry.getKey() + " - " + lastEntry.getValue());
  • Iterando um TreeMap
// Navega nos registros do mapa
Pedimos o entrySet(), o qual retorna um set na variável entry.
E por esta variável, conseguimos pegar a chave(getKey()) e o valor(getValue()).
​
// Navega em todos as chaves do iterator
Iterator<String> iterator = treeCapitais.keySet().iterator();
​
while (iterator.hasNext()) {
 String key = iterator.next();
 System.out.println(key + " - " + treeCapitais.get(key));
}
​
for (String capital: treeCapitais.keySet()) {
 System.out.println(capital + " -- " +          treeCapitais.get(capital));
}
​
for (Map.Entry<String, String> capital: treeCapitais.entrySet()) {
 System.out.println(capital.getKey() + " --- " + capital.getValue());
}
  • Hashtable
  • É utilizado quando existe concorrência de Threads.
  • Declarando um Hashtable
Hashtable<tipoChave, tipoValor> nomeHashtable = new Hashtable<>();
​
Exemplo:
Hashtable<String, Integer> estudantes = new Hashtable<>();
  • Métodos
  • put(chave, valor)
  • Adiciona uma chave e valor ao Hashtable.
  • Exemplo:
estudantes.put("Carlos", 21);
  • get(chave)
  • Pega o valor associado a chave passa no parâmetro.
  • Exemplo:
int idadeEstudante = estudantes.get("Mariana");
  • remove(chave)
  • Remove a chave e seu valor.
  • Exemplo:
estudantes.remove("Pedro");
  • size()
  • Retorna um inteiro com o número de elementos chave-valor no Hashtable.
  • Exemplo:
System.out.println(estudantes.size());
  • Iterando um Hashtable
// Navega nos registros do mapa
for (Map.Entry<String, Integer> entry : estudantes.entrySet()) {
 System.out.println(entry.getKey() + " -- " + entry.getValue());
}
​
// Navega nos registros do mapa
for (String key : estudantes.keySet()) {
 System.out.println(key + " -- " + estudantes.get(key));
}

Comparators e Comparables

  • São interfaces utilizadas para comparar objetos de domínio, onde definimos uma regra de negócio para ordenar uma coleção.
  • Comparable
  • Esta interface deve ser implementada em nossa classe de domínio e então devemos sobrescrever o método compareTo() como podemos ver no exemplo abaixo:
public class Estudante implements Comparable<Estudante> {
​
  private final String nome;
  private final Integer idade;
​
  public Estudante(String nome, Integer idade) {
    this.nome = nome;
    this.idade = idade;
 }
​
  public String getNome() {
    return nome;
 }
​
  public Integer getIdade() {
    return idade;
 }
​
  @Override
  public String toString() {
    return nome + " - " + idade;
 }
​
  @Override
  public int compareTo(Estudante o) {
    return this.getIdade() - o.getIdade();
 }
}
  • Dado os seguintes objetos:
List<Estudante> estudantes = new ArrayList<>();
​
estudantes.add(new Estudante("Pedro", 19));
estudantes.add(new Estudante("Carlos", 23));
estudantes.add(new Estudante("Mariana", 21));
estudantes.add(new Estudante("João", 18));
estudantes.add(new Estudante("Thiago", 20));
estudantes.add(new Estudante("George", 22));
estudantes.add(new Estudante("Larissa", 21));
  • Comparando objetos que implementam a classe Comparable:
  • Podemos comparar estes objetos através do método sort() da classe Collections:


Collections.sort(estudantes);
  • *Este método só irá funcionar se a classe que você está tentando fazer a ordenação implementar a interface Comparable.
  • Comparator
  • Diferente da interface Comparable, onde a implementamos diretamente em nossa classe de domínio, aqui criamos uma outra classe para abrigar os métodos de comparação. Ainda utilizando a Classe Estudante, temos o seguinte exemplo:
public class EstudanteOrdemIdadeReversaComparator implements Comparator<Estudante> {
@Override
public int compare(Estudante o1, Estudante o2) {
   return o2.getIdade() - o1.getIdade();
 }  
}
  • Aqui criamos a classe EstudanteOrdemIdadeReversaComparator, a qual implementa a interface Comparator, recebendo a nossa classe Estudante por parâmetro. Se faz necessário a sobrescrita do método compare para criarmos nossa regra de ordenação.


  • Comparando elementos com Comparator:
  • Utilizando Colletions.sort()
Collections.sort(estudantes, new EstudanteOrdemIdadeReversaComparator());
  • Aqui passamos como primeiro parâmetro nossa lista de estudantes e como segundo parâmetro, uma nova instância da classe que implementa a interface Comparator.
  • Podemos também utilizar os métodos da interface Comparator através do método sort() que as listas possuem:
Aqui estamos ordenando pela idade do atual e do próximo estudante até que o método passe por todos os elementos da lista. O padrão é de valores crescentes
​
estudantes.sort(Comparator.comparingInt(Estudante::getIdade));
​
​
Podemos fazer valores decrscentes através de:
estudantes.sort(Comparator.comparingInt(Estudante::getIdade).reversed());
  • Utilizando Lambdas para comparar
  • Como as Listas possuem o método sort(), podemos utilizar lambdas para fazer a comparação e ordenação. Ainda utilizando as classes criadas acima temos:
estudantes.sort((first, second) -> first.getIdade() - second.getIdade());
  • Para ordenar com lambda, se faz necessário a utilização de dois parâmetros(first e second). Se o resultado da subtração for negativo, significa que second deve ficar a esquerda de first. Caso o resultado der positivo, significa que first deve ficar a direita de second. E se o resultado for zero, então não há alteração na ordem. Ou seja: Retornarmos um resultado menor ou igual a -1 para alocar o objeto a esquerda, igual à 0 para manter a ordem e maior ou igual a 1 para alocar o objeto a direta.

Optional

  • Permite que sejam executadas operações em valores que podem ser nulos sem preocupação com NullPointerExceptions.
  • Declarando um Optional
  • Optional.of
  • Cria um optional com um valor. Caso o valor passado no parâmetro seja nulo, lançara um NullPointerException.
Optional<Tipo> nomeOptional = Optional.of(valorDoOptional);
​
Exemplo:
Optional<String> optionalString = Optional.of("Valor presente");
  • Optional.ofNullable
  • Cria um optional com um valor que caso seja nulo, retorna um Optional vazio.
Optional<tipo> nomeOptional = Optional.ofNullable(valor);
​
Exemplo:
Optional<String> optionalNull = Optional.ofNullable(null);
  • Optional.empty
  • Cria um optional com um valor vazio.
Optional<tipo> nomeOptional = Optional.empty();
​
Exemplo:
Optional<String> emptyOptional = Optional.empty();
  • Métodos Optional
  • isPresent()
  • Retorna True se o Optional possue um valor ou False caso não possua.
  • Exemplo:
Optional<String> optionalString = Optional.empty();
​
//Retorna False
System.out.println(optionalString.isPresent());
  • isEmpty()
  • Retorna True se o Optional está vazio ou False caso possua valor.
  • Exemplo:
Optional<String> optionalString = Optional.empty();
​
//Retorna True
System.out.println(optionalString.isEmpty());
  • ifPresentOrElse()
  • Se o valor do Optional estiver presente, faz uma determinada ação. Caso não esteja, faz outra.
  • Exemplo: Se o valor estiver presente, printa na tela o valor. Caso não esteja ou seja nulo, printa a mensagem "não está presente".
System.out.println("Valor opcional que está presente");
   optionalString.ifPresentOrElse(System.out::println, () -> System.out.println("não está presente"));
  • Códigos Utilizados na Aula:
Optional<String> optionalString = Optional.of("Valor presente");
​
System.out.println("Valor opcional que está presente");
optionalString.ifPresentOrElse(System.out::println, () -> System.out.println("não está presente"));
​
Optional<String> optionalNull = Optional.ofNullable(null);
​
System.out.println("Valor opcional que não está presente");
optionalNull.ifPresentOrElse(System.out::println, () -> System.out.println("null = não está presente"));
​
Optional<String> emptyOptional = Optional.empty();
​
System.out.println("Valor opcional que não está presente");
emptyOptional.ifPresentOrElse(System.out::println, () -> System.out.println("empty = não está presente"));
​
Optional<String> optionalNullErro = Optional.of(null);
​
System.out.println("Valor opcional que lança erro NullPointerException");
optionalNullErro.ifPresentOrElse(System.out::println, () -> System.out.println("erro = não está presente"));
​
optionalString = Optional.empty();
​
   System.out.println(optionalString.isPresent());
   System.out.println(optionalString.isEmpty());
​
   optionalString.ifPresent(System.out::println);
​
   optionalString.ifPresentOrElse(System.out::println,
       () -> System.out.println("Valor não está presente"));
​
   if (optionalString.isPresent()) {
     String valor = optionalString.get();
​
     System.out.println(valor);
   }
​
   optionalString.map((valor) -> valor.concat("****")).ifPresent(System.out::println);
​
   System.out.println(optionalString.orElseThrow(IllegalStateException::new));
​
  • Optionals Primitivos
  • Através das bibliotecas java.util.OptionalDouble, java.util.OptionalInt e java.util.OptionalLong, podemos criar optional para os tipos primitivos Int, Double e Long.
  • Exemplo:
System.out.println("***Valor inteiro opcional***");
   OptionalInt.of(12).ifPresent(System.out::println);
​
   System.out.println("***Valor decimal opcional***");
   OptionalDouble.of(55.0).ifPresent(System.out::println);
​
   System.out.println("***Valor longo opcional***");
   OptionalLong.of(23L).ifPresent(System.out::println);

Stream API

  • Utilizadas para manipular coleções com paradigma funcional e de forma paralela, os streams nos trazem mais performance e tornam o código mais declarativo e fácil de ler. Por ser imutável, não altera a coleção de origem, pois sempre cria uma nova.
  • Pode ser usado em qualquer objeto que implementa a interface Collections, chamando o método stream() através da coleção.
  • Dado o ArrayList abaixo, vamos falar sobre aos métodos do Stream:
List<String> estudantes = new ArrayList<>();
​
// Adiciona 4 estudantes para a coleção
estudantes.add("Pedro");
estudantes.add("Thayse");
estudantes.add("Marcelo");
estudantes.add("Carla");
estudantes.add("Juliana");
estudantes.add("Thiago");
estudantes.add("Rafael");
  • count()
  • Retorna um inteiro que representa a contagem de elementos.
  • Exemplo:
// Retorna a contagem de elementos do stream
System.out.println("Contagem: " + estudantes.stream().count());
  • max()
  • Recebe um Comparator como parâmetro e retorna um optional que representa o maior elemento da stream.
  • Exemplo:
// Retorna o elemento com maior numero de letras
​
System.out.println("Maior numero de letras: " + estudantes.stream().max(Comparator.comparingInt(String::length)));
  • min()
  • Recebe um Comparator como parâmetro e retorna um optional que representa o menor elemento da stream.
  • Exemplo:
// Retorna o elemento com menor numero de letras
​
System.out.println("Menor numero de letras: " + estudantes.stream().min(Comparator.comparingInt(String::length)));
  • filter() e collect()
  • O método filter() como o próprio nome diz, filtra a coleção com base no predicado passado como parâmetro.
  • Já o método collect() faz uma redução da coleção na stream e retorna o resultado desta redução. No exemplo abaixo, transformamos o que foi coletado em uma lista.
  • Exemplo:
// Retorna os elementos que tem a letra R no nome
​
System.out.println("Com a letra r no nome: " + estudantes.stream()
.filter((estudante) -> estudante.toLowerCase().contains("r"))
.collect(Collectors.toList())
);
  • limit()
  • Recebe como parâmetro o limite de resultados que a coleção deve ter e retorna uma coleção com elementos limitados pelo parâmetro.
  • Exemplo:
// Retorna somente os 3 primeiros elementos da coleção
​
System.out.println("Retorna os 3 primeiros elementos: " + estudantes.stream().limit(3).collect(Collectors.toList()));
  • Peek
  • Executa uma determinada lógica para cada elemento, retornando a própria coleção.
  • Exemplo:
// Exibe cada elemento no console, e depois retorna a mesma coleção
System.out.println("Retorna os elementos: " + estudantes.stream().peek(System.out::println).collect(Collectors.toList()));
  • allMatch()
  • Recebe um predicado que retorna um booleano. O método retornará um booleano True se TODOS os elementos retornarem True para o predicado.
  • Exemplo:
 // Retorna true se todos os elementos possuem a letra W no nome
 
System.out.println("Tem algum elemento com W no nome? " + estudantes.stream().allMatch((elemento) -> elemento.contains("W")));
  • anyMatch()
  • Recebe um predicado que retorna um booleano. O método retornará um booleano True se ALGUM dos elementos retornarem True para o predicado.
  • Exemplo:
// Retorna true se algum os elementos possuem a letra a minúscula no nome
​
System.out.println("Tem algum elemento com a minúscula no nome? " + estudantes.stream().anyMatch((elemento) -> elemento.contains("a")));
  • noneMatch()
  • Este método é o oposto do método allMatch(). Recebe um predicado que retorna um booleano. O método retornará um booleano True se NENHUM dos elementos retornarem True para o predicado.
  • Exemplo:
// Retorna true se nenhum elemento possue a letra a minúscula no nome
​
System.out.println("Não tem nenhum elemento com a minúscula no nome? " + estudantes.stream().noneMatch((elemento) -> elemento.contains("a")));
  • findFirst()
  • Retorna um Optional que representa o primeiro elemento da Stream. Junto com ele podemos utilizar o método ifPresent() que caso o valor recrutado pelo método findFirst() não seja um Optional vazio, então é performada uma ação.
  • Exemplo:
// Retorna o primeiro elemento da coleção, se existir exibe no console
​
estudantes.stream().findFirst().ifPresent(System.out::println);
  • Map()
  • Recebe uma função como parâmetro e retorna uma Stream com mesmo tamanho da origem e com os elementos alterados.
  • Exemplo:
//Para cada estudante na stream, é concatenado seu nome com uma string e o tamanho de seu nome e depois é retornado uma nova lista com os valores alterados:
​
estudantes.stream()
.map(estudante -> estudante.concat(" - ")
.concat(String.valueOf(estudante.length())))
.collect(Collectors.toList());
  • ForEach
  • Método sem retorno que executa uma determina lógica para cada elemento.
  • Exemplo:
// Exibe cada elemento no console sem retornar outra coleção
​
estudantes.stream().forEach(System.out::println);
  • Collectors
  • Quando chegamos na etapa de coletar a coleção após sua manipulação, podemos utilizar alguns métodos da interface Collectors para simplificar nosso código.
  • Collectors.toList()
  • Transforma a Stream de dados em uma lista.
  • Exemplo:
estudantes.stream()
.filter((estudante) -> estudante.toLowerCase().contains("r"))
.collect(Collectors.toList()));
  • Collectors.joining(delimitador)
  • Recebe um delimitador como parâmetro e retorna a concatenação do delimitador com todos os valores.
  • Exemplo:
estudantes.stream()
.filter((estudante) -> estudante.toLowerCase().contains("r"))
.collect(Collectors.joining(", ")));
​
//Resultado:
//Pedro, Marcelo, Carla, Rafael
  • Collectors.toSet()
  • Transforma a Stream de dados em um Set.
  • Exemplo:
estudantes.stream()
.filter((estudante) -> estudante.toLowerCase().contains("r"))
.collect(Collectors.toSet()));
  • Collectors.groupingBy()
  • Recebe uma função e retorna um Collector agrupado.
  • Exemplo:
estudantes.stream()
.map(estudante -> 
estudante.concat(" - ").concat(String.valueOf(estudante.length())))
.collect(Collectors.groupingBy(estudante -> estudante.substring(estudante.indexOf("-") + 1)))
​
//Resultado:
//{ 5=[Pedro - 5, Carla - 5], 6=[Thayse - 6, Thiago - 6, Rafael - 6], 
7=[Marcelo - 7, Juliana - 7]}


0
0

Comentários (4)

0
Maria Moura

Maria Moura

25/09/2021 23:00

Super útil. Fiz es módulo, mas perdi todas as anotações. Então agora estou revisando tudo pelo seu resumo, ajudando no Bootcamp que estou fazendo agora. Obrigada!

1
Mateus Silva

Mateus Silva

11/08/2021 11:33

Bem isso kkkk ter que codar com LISP, uma linguagem poderosa, mas haja parênteses para escrever um código simples.

0
Vinicius Almeida

Vinicius Almeida

11/08/2021 11:27

Streams deixam o código muito mais descritivo. Você bate o olho e já sabe o que está acontecendo. Falou em LISP e me veio umas memórias bem tristes sobre aprender perceptrons com essa linguagem arcaica kkkkkkk

1
Mateus Silva

Mateus Silva

11/08/2021 09:40

Muito bom resumo, Vinicius. Desde que vi a aula de Java Avançado e Collections uso em quase todas aplicações a Stream API e expressões Lambda. As collections já conhecia e usava, são extremamente úteis porque já trazem uma boa abstração para as estruturas de dados ou para criar novas.


Realmente é uma boa a parte de programação funcional ter sido inserida no Java fica muito mais robusto, na faculdade aprendi primeiro com LISP que tem mais parênteses do quê qualquer outra coisa.

https://www.linkedin.com/in/vinicius-ornelas-587075128/

Brasil