CodeGym /Blogue Java /Random-PT /Excluindo um elemento de um ArrayList
John Squirrels
Nível 41
San Francisco

Excluindo um elemento de um ArrayList

Publicado no grupo Random-PT
Oi! Na última lição, nos familiarizamos com a ArrayListclasse e aprendemos como realizar as operações mais comuns com esta classe. Além disso, apontamos várias diferenças entre um ArrayListe um array comum. Mas contornamos um tópico, ou seja, como excluir elementos de um arquivo ArrayList. Discutiremos isso agora. Excluindo um elemento de um ArrayList - 1Já mencionamos que excluir elementos de um array comum não é muito conveniente. Como não podemos excluir o elemento em si, podemos apenas "zerar" (definir como nulo) seu valor:
public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat[] cats = new Cat[3];
       cats[0] = new Cat("Thomas");
       cats[1] = new Cat("Behemoth");
       cats[2] = new Cat("Lionel Messi");

       cats[1] = null;

       System.out.println(Arrays.toString(cats));
   }


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Saída: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] Mas definir um elemento de matriz como nulo deixa um "buraco". Não removemos a posição no array, apenas seu conteúdo. Imagine o que aconteceria se tivéssemos uma matriz de 50 gatos e removêssemos 17 deles dessa maneira. Teremos uma matriz com 17 buracos. Apenas tente acompanhá-los! Não é realista esperar lembrar o número de células vazias onde você pode escrever novos valores. Se você cometer um erro, substituirá uma referência de objeto que deseja. Existe, claro, uma forma de fazer isso com um pouco mais de cuidado: após remover um elemento, mova os elementos para a frente do array para colocar o "buraco" no final:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Behemoth");
   cats[2] = new Cat("Lionel Messi");
   cats[2] = new Cat("Fluffy");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       cats [i-1] = cats [i];// Move the elements to the front of the array, so the empty position is at the end
   }

   System.out.println(Arrays.toString(cats));
}
Saída: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] Isso parece melhor, mas dificilmente pode ser chamado de solução robusta. Pelo menos pelo fato de termos que escrever esse código toda vez que excluímos um elemento de um array! Esta é uma má opção. Poderíamos seguir outro caminho e criar um método separado:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move the elements
}
Mas isso também é de pouca utilidade: esse método pode funcionar com Catobjetos, mas não com outros tipos. Em outras palavras, se um programa tiver outras 100 classes que queremos usar com arrays, teremos que escrever o mesmo método com exatamente a mesma lógica em cada uma delas. Isso é um desastre total -_- Mas a ArrayListturma resolve esse problema! Ele implementa um método especial para remover elementos:remove()
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Passamos o índice do nosso objeto para o método, que o deleta (como em um array). O remove()método tem duas características especiais. Primeiro, não deixa "buracos". Ele já implementa a lógica necessária para deslocar elementos quando um elemento é removido do meio, que nós mesmos escrevemos anteriormente. Veja a saída do código anterior:
[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
Retiramos um gato do meio e os demais foram movidos para que não houvesse espaços vazios. Em segundo lugar , ele pode excluir objetos não apenas por índice (como uma matriz normal), mas também por referência :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);
   System.out.println(cats.toString());

   cats.remove(lionel);

   System.out.println(cats.toString());
}
Saída: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Isso pode ser muito conveniente se você não quiser sempre acompanhar o índice do objeto desejado. Parece que descobrimos a exclusão comum. Agora vamos imaginar esta situação: queremos iterar nossa lista e remover um gato com um nome específico . Para fazer isso, usaremos um forloop rápido (também chamado de loop for-each), que aprendemos nas aulas de Rishi:
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Behemoth");
   Cat lionel = new Cat("Lionel Messi");
   Cat fluffy = new Cat ("Fluffy");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(lionel);
   cats.add(fluffy);

   for (Cat cat: cats) {

       if (cat.name.equals("Behemoth")) {
           cats.remove(cat);
       }
   }

   System.out.println(cats);
}
O código parece perfeitamente lógico. Mas o resultado pode ser uma grande surpresa: Exceção no encadeamento "principal" java.util.ConcurrentModificationException em java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) em java.util.ArrayList$Itr.next(ArrayList. java:831) em Cat.main(Cat.java:25) Há algum tipo de erro e não está claro por que ocorreu. Este processo envolve uma série de nuances que devem ser abordadas. Aqui está a regra geral que você precisa lembrar: você não pode iterar simultaneamente em uma coleção e alterar seus elementos. E queremos dizer qualquer tipo de mudança, não apenas remoção. Se você substituir a remoção do gato por uma tentativa de inserir novos gatos, o resultado será o mesmo:
for (Cat cat: cats) {

   cats.add(new Cat("Salem Saberhagen"));
}

System.out.println(cats);
Exceção no encadeamento "principal" java.util.ConcurrentModificationException em java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) em java.util.ArrayList$Itr.next(ArrayList.java:831) em Cat.main( Cat.java:25) Mudamos uma operação para outra, mas o resultado não mudou: obtemos a mesma ConcurrentModificationException . Ocorre justamente quando tentamos quebrar a regra acima alterando a lista enquanto iteramos sobre ela. Em Java, precisamos de um objeto especial chamado iterador (Iteratorclasse) para excluir itens durante a iteração de uma coleção. AIteratorclasse é responsável por iterar com segurança na lista de elementos. É bastante simples, pois possui apenas 3 métodos:
  • hasNext()- retorna verdadeiro ou falso, dependendo se há um próximo item na lista ou se já chegamos ao último.
  • next()- retorna o próximo item da lista
  • remove()- remove um item da lista
Excluindo um elemento de um ArrayList - 2Como você pode ver, o iterador é feito sob medida para nossas necessidades e, ao mesmo tempo, não há nada complicado nisso. Suponha que queremos verificar se há um próximo elemento em nossa lista e exibi-lo se houver:
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list

   Cat nextCat = catIterator.next();// Get the next element
   System.out.println(nextCat);// Display it
}
Saída: Cat{name='Thomas'} Cat{name='Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} Como você pode ver, o já implementou um método ArrayListespecial para criar um iterador: iterator(). Além disso, observe que, ao criar um iterador, especificamos a classe de objetos com a qual ele irá trabalhar ( <Cat>). O ponto principal é que um iterador lida facilmente com nossa tarefa original. Por exemplo, remova o gato chamado "Lionel Messi":
Iterator<Cat> catIterator = cats.iterator();// Create an iterator
while(catIterator.hasNext()) {// As long as there are elements in the list

   Cat nextCat = catIterator.next();// Get the next element
   if (nextCat.name.equals("Lionel Messi")) {
       catIterator.remove();// Delete the cat with the specified name
   }
}

System.out.println(cats);
Saída: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] Você deve ter notado que não especificamos nem o índice nem o nome no remove()método do iterador ! O iterador é mais inteligente do que parece: remove()remove o último elemento retornado pelo iterador. Como você pode ver, ele fez exatamente o que queríamos que fizesse :) Em princípio, isso é tudo o que você precisa saber sobre como remover elementos de um arquivo ArrayList. Bem, quase tudo. Na próxima lição, veremos dentro desta classe e veremos o que acontece durante várias chamadas de método :) Até lá!
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION