JavaRush /Blogue Java /Random-PT /Removendo um elemento de um ArrayList em Java

Removendo um elemento de um ArrayList em Java

Publicado no grupo Random-PT
Olá! Na última palestra conhecemos a classe ArrayList e também aprendemos como realizar as operações mais comuns com ela. Além disso, destacamos algumas diferenças entre ArrayList e um array regular. Agora vamos ver como remover um elemento do ArrayList. Já dissemos que excluir elementos de um array regular não é muito conveniente. Removendo um elemento de um ArrayList - 1Como não podemos deletar a célula em si, só podemos “zerar” 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("Hippopotamus");
       cats[2] = new Cat("Philip Markovich");

       cats[1] = null;

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


@Override
   public String toString() {
       return "Cat{" +
               "name='" + name + '\'' +
               '}';
   }
}
Conclusão:

[Cat{name='Томас'}, null, Cat{name='Фorпп Маркович'}]
Mas quando reiniciado, um “buraco” permanece na matriz. Não estamos excluindo a célula, mas apenas seu conteúdo. Imagine o que acontecerá se tivermos um array de 50 gatos, 17 dos quais excluímos desta forma. Teremos uma matriz com 17 buracos e cuidaremos deles! Lembrar de cor os números de células vazias onde você pode escrever novos valores não é realista. Cometa um erro uma vez e você substituirá a célula pela referência desejada ao objeto. Existe, claro, uma oportunidade de fazer isso com um pouco mais de cuidado: após deletar, mova os elementos do array para o início, de forma que o “buraco” fique no final:
public static void main(String[] args) {

   Cat[] cats = new Cat[4];
   cats[0] = new Cat("Thomas");
   cats[1] = new Cat("Hippopotamus");
   cats[2] = new Cat("Philip Markovich");
   cats[3] = new Cat("Fluff");

   cats[1] = null;

   for (int i = 2; i < cats.length-1; i++) {
       //move the elements to the beginning so that the empty cell is at the end
       cats[i-1] = cats[i];
       cats[i] = null;
   }

   System.out.println(Arrays.toString(cats));
}
Conclusão:

[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}, null]
Agora parece melhor, mas dificilmente pode ser chamada de solução estável. No mínimo, porque teremos que escrever esse código manualmente toda vez que removermos um elemento do array! Opção ruim. Você poderia seguir o outro caminho e criar um método separado:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift the elements
}
Mas isso também é de pouca utilidade: esse método só pode funcionar com objetos Cat, mas não pode funcionar com outros. Ou seja, se houver mais 100 classes no programa com as quais queremos utilizar arrays, teremos que escrever o mesmo método com exatamente a mesma lógica em cada uma delas. Isso é um fracasso total -_- Mas na classe ArrayList esse problema foi resolvido com sucesso! 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("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(1);

   System.out.println(cats.toString());
}
Passamos o índice do nosso objeto para o método e ele foi deletado (assim como em um array). O método remove()possui dois recursos. Em primeiro lugar , não deixa “buracos”. Já implementa a lógica de deslocamento de elementos ao retirar um elemento do meio, que anteriormente escrevemos à mão. Veja a saída do código anterior no console:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
Retiramos um gato do meio e os outros foram movimentados para que não houvesse lacunas. Em segundo lugar , ele pode excluir um objeto não apenas por índice (como um array normal), mas também por referência ao objeto :
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);
   System.out.println(cats.toString());

   cats.remove(philipp);

   System.out.println(cats.toString());
}
Conclusão:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Isso pode ser muito conveniente se você não quiser manter sempre o índice do objeto desejado em sua cabeça. Parece que resolvemos a exclusão usual. Agora vamos imaginar esta situação: queremos percorrer nossa lista de elementos e remover um gato com um determinado nome. Para isso usamos um operador de loop especial for- for each. Você pode aprender mais sobre isso nesta palestra .
public static void main(String[] args) {

   ArrayList<Cat> cats = new ArrayList<>();
   Cat thomas = new Cat("Thomas");
   Cat behemoth = new Cat("Hippopotamus");
   Cat philipp = new Cat("Philip Markovich");
   Cat pushok = new Cat("Fluff");

   cats.add(thomas);
   cats.add(behemoth);
   cats.add(philipp);
   cats.add(pushok);

   for (Cat cat: cats) {

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

   System.out.println(cats);
}
O código parece bastante lógico. No entanto, o resultado pode surpreendê-lo:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Algum tipo de erro e não está claro por que apareceu de repente. Há uma série de nuances neste processo que precisam ser tratadas. Uma regra geral que você precisa lembrar: você não pode iterar em uma coleção e alterar seus elementos ao mesmo tempo. Sim, sim, exatamente uma mudança, e não apenas uma exclusão. Se você tentar em nosso código substituir a remoção de gatos pela inserção de novos, o resultado será o mesmo:
for (Cat cat: cats) {

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

System.out.println(cats);

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
  at java.util.ArrayList$Itr.next(ArrayList.java:831)
  at Cat.main(Cat.java:25)
Trocamos uma operação por outra, mas o resultado não mudou: o mesmo erro ConcurrentModificationException. Ocorre precisamente quando tentamos quebrar uma regra e alterar a lista enquanto iteramos por ela. Em Java, para remover elementos durante a iteração, você precisa usar um objeto especial - um iterador (classe Iterator). A turma Iteratoré responsável por percorrer com segurança uma lista de elementos. É bastante simples porque possui apenas 3 métodos:
  • hasNext()- retorna truedependendo falsese existe um próximo elemento na lista ou se já alcançamos o último.
  • next()- retorna o próximo elemento da lista
  • remove()- remove um elemento da lista
Como você pode ver, o iterador é literalmente “adaptado” às nossas necessidades e não há nada de complicado nisso. Por exemplo, queremos verificar se nossa lista contém o seguinte elemento e, em caso afirmativo, imprimi-lo no console:
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 next element
   System.out.println(nextCat);// print it to the console
}
Conclusão:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фorпп Маркович'}
Cat{name='Пушок'}
Como você pode ver, a classe ArrayListjá implementa um método especial para criar um iterador - iterator(). Além disso, observe que ao criar um iterador, especificamos a classe de objetos com os quais ele irá trabalhar ( <Cat>). Em última análise, podemos resolver facilmente nosso problema original usando um iterador. Por exemplo, vamos deletar um gato chamado “Philip Markovich”:
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 next element
   if (nextCat.name.equals("Philip Markovich")) {
       catIterator.remove();//delete the cat with the desired name
   }
}

System.out.println(cats);
Conclusão:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
Você deve ter notado que não especificamos nem o índice do elemento nem o nome da variável de referência no método iterador remove()! O iterador é mais inteligente do que parece: o método remove()remove o último elemento retornado pelo iterador. Como você pode ver, funcionou exatamente conforme necessário :) Isso é basicamente tudo que você precisa saber sobre como remover elementos do ArrayList. Mais precisamente, quase tudo. Na próxima palestra vamos dar uma olhada no “interior” desta aula e ver o que acontece lá durante as operações :) Até mais!
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION