Removing Elements from Java Collections

Overview

Neste tutorial rápido, vamos falar sobre quatro maneiras diferentes de remover itens de Java Collections que correspondem a certas previsões.

Naturalmente também vamos olhar para algumas das limitações.

Definindo nossa coleção

Primeiro, vamos ilustrar duas abordagens que alteram a estrutura original dos dados. Depois vamos falar de duas outras opções que ao invés de remover os itens, criaremos uma cópia da Coleção original sem eles.

Vamos usar a seguinte coleção ao longo dos nossos exemplos para demonstrar como podemos alcançar o mesmo resultado usando métodos diferentes:

Collection<String> names = new ArrayList<>();names.add("John");names.add("Ana");names.add("Mary");names.add("Anthony");names.add("Mark");

Elementos Removentes com Iterador

O Iterador da Java nos permite caminhar e remover cada elemento individual dentro de uma Coleção.

Para isso, precisamos primeiro recuperar um iterador sobre os seus elementos usando o método do iterador. Depois, podemos visitar cada elemento com a ajuda do próximo e removê-los usando remove:

Iterator<String> i = names.iterator();while(i.hasNext()) { String e = i.next(); if (e.startsWith("A")) { i.remove(); }}

Apesar da sua simplicidade, há algumas advertências que devemos considerar:

  • Dependente da coleção podemos encontrar exceções de Modificação ConcorrenteExceções
  • Precisamos iterar sobre os elementos antes de podermos removê-los
  • Dependendo da coleção, remove pode ter um comportamento diferente do esperado. Por exemplo: O ArrayList.Iterator remove o elemento da colecção e desloca os dados subsequentes para a esquerda, enquanto o LinkedList.Iterator simplesmente ajusta o ponteiro para o elemento seguinte. Como tal, o LinkedList.Iterator tem um desempenho muito melhor que o ArrayList.Iterator ao remover itens

Java 8 e Collection.removeIf()

Java 8 introduziu um novo método na interface Collection que fornece uma forma mais concisa de remover elementos usando Predicate:

names.removeIf(e -> e.startsWith("A"));

É importante notar que ao contrário da abordagem Iterator, removeIf tem um desempenho semelhante tanto no LinkedList como no ArrayList.

Em Java 8, ArrayList anula a implementação padrão – que se baseia no Iterator – e implementa uma estratégia diferente: primeiro, itera sobre os elementos e marca os que correspondem ao nosso Predicate; depois, itera uma segunda vez para remover (e deslocar) os elementos que foram marcados na primeira iteração.

Java 8 e a Introdução do Stream

Uma das novas características principais do Java 8 foi a adição do Stream (e dos Colectores). Há muitas maneiras de criar um Stream a partir de uma fonte. No entanto, a maioria das operações que afetam a instância Stream não mudam seu código fonte, ao contrário, a API foca em criar cópias de um código fonte e realizar qualquer operação que possamos precisar neles.

Vejamos como podemos usar Stream e Coletores para encontrar/filtrar elementos que combinam, e não combinam, com nosso Predicate.

5.1. Removendo Elementos com Stream

Remover, ou melhor, filtrar elementos usando Stream é bastante simples, só precisamos criar uma instância de Stream usando nossa coleção, invocar o filtro com nosso Predicate e então coletar o resultado com a ajuda de Collectors:

Collection<String> filteredCollection = names .stream() .filter(e -> !e.startsWith("A")) .collect(Collectors.toList());

Streaming é menos invasivo que as abordagens anteriores, promove o isolamento e permite a criação de múltiplas cópias a partir da mesma fonte. No entanto, devemos ter em mente que também aumenta a memória utilizada pela nossa aplicação.

5.2. Collectors.partitioningBy

Combinar tanto Stream.filter como Collectors é bastante útil, embora possamos deparar-nos com cenários onde precisamos de elementos que combinem e não combinem. Nesses casos podemos tirar vantagem do Collectors.partitioningBy:

Map<Boolean, List<String>> classifiedElements = names .stream() .collect(Collectors.partitioningBy((String e) -> !e.startsWith("A")));String matching = String.join(",", classifiedElements.get(true));String nonMatching = String.join(",", classifiedElements.get(false));

Este método retorna um Mapa que contém apenas duas chaves, verdadeira e falsa, cada uma apontando para uma lista que contém os elementos correspondentes e não correspondentes, respectivamente.

Conclusion

Neste artigo, vimos alguns métodos para remover elementos de Colecções e algumas das suas advertências.

Deixe uma resposta

O seu endereço de email não será publicado.