JavaRush /Java 博客 /Random-ZH /在 Java 中从 ArrayList 中删除元素

在 Java 中从 ArrayList 中删除元素

已在 Random-ZH 群组中发布
你好!在上一讲中,我们熟悉了ArrayList类,并学习了如何使用它执行最常见的操作。此外,我们还强调了 ArrayList 和常规数组之间的一些差异。现在让我们看看从 ArrayList 中删除一个元素。我们已经说过,删除常规数组中的元素并不是很方便。从 ArrayList 中删除一个元素 - 1由于我们无法删除单元格本身,因此我们只能将其值“归零”:
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 + '\'' +
               '}';
   }
}
结论:

[Cat{name='Томас'}, null, Cat{name='Фorпп Маркович'}]
但重置后,阵列中仍留有一个“洞”。我们不会删除单元格,而只会删除其内容。想象一下,如果我们有一个包含 50 只猫的数组,我们以这种方式删除了其中 17 只,会发生什么。我们将有一个有 17 个孔的阵列,并照顾它们!记住可以写入新值的空单元格的数量是不现实的。犯一次错误,您就会用所需的对象引用覆盖单元格。当然,有机会做得更仔细一点:删除后,将数组的元素移到开头,这样“洞”就在末尾:
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));
}
结论:

[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}, null]
现在看起来似乎更好了,但这很难称为稳定的解决方案。至少,因为每次从数组中删除元素时,我们都必须手动编写此代码!糟糕的选择。您可以采用另一种方式并创建一个单独的方法:
public void deleteCat(Cat[] cats, int indexToDelete) {
   //...remove the cat by index and shift the elements
}
但这也没什么用处:这个方法只能对对象起作用Cat,而不能对其他对象起作用。也就是说,如果程序中还有 100 个以上的类,我们要使用数组,则必须在每个类中编写相同的方法,并具有完全相同的逻辑。这是彻底的失败-_- 但是在ArrayList类中这个问题被成功解决了!它实现了一种删除元素的特殊方法 - 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());
}
我们将对象的索引传递给该方法,然后它被删除(就像在数组中一样)。该方法remove()有两个特点。 首先,它不留“漏洞”。它已经实现了从中间删除元素时移动元素的逻辑,这是我们之前手写的。在控制台中查看前面代码的输出:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
我们从中间移走了一只猫,然后将其他猫移到周围,以确保没有间隙。 其次,它不仅可以通过索引(如常规数组)删除对象,还可以通过对象的引用来删除对象
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());
}
结论:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Фorпп Маркович'}, Cat{name='Пушок'}]
[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
如果您不想始终将所需对象的索引保留在脑海中,这会非常方便。我们似乎已经解决了通常的删除问题。现在让我们想象一下这种情况:我们想要迭代元素列表并删除具有特定名称的猫。 为此,我们使用一个特殊的循环运算符for- for each您可以在本次讲座中了解更多相关信息。
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);
}
该代码看起来非常合乎逻辑。然而,结果可能会让你大吃一惊:

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)
某种错误,并且不清楚为什么会突然出现。在此过程中有许多细微差别需要处理。您需要记住的一般规则: 您不能迭代集合并同时更改其元素。 是的,是的,确实是改变,而不仅仅是删除。如果您尝试在我们的代码中将删除猫替换为插入新猫,结果将是相同的:
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)
我们将一项操作更改为另一项操作,但结果没有改变:同样的错误ConcurrentModificationException。当我们在迭代列表时尝试打破规则并更改列表时,就会发生这种情况。在Java中,要在迭代期间删除元素,您需要使用一个特殊的对象 - 迭代器(类Iterator)。该类Iterator负责安全地遍历元素列表。它非常简单,因为它只有 3 个方法:
  • hasNext()- 返回true取决于false列表中是否有下一个元素,或者我们是否已经到达最后一个元素。
  • next()- 返回列表的下一个元素
  • remove()- 从列表中删除一个元素
正如您所看到的,迭代器实际上是根据我们的需求“定制”的,并且没有什么复杂的。例如,我们要检查列表是否包含以下元素,如果包含,则将其打印到控制台:
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
}
结论:

Cat{name='Томас'}
Cat{name='Бегемот'}
Cat{name='Фorпп Маркович'}
Cat{name='Пушок'}
正如您所看到的,该类ArrayList已经实现了一个用于创建迭代器的特殊方法 - iterator()。另请注意,在创建迭代器时,我们指定了它将使用的对象类 ( <Cat>)。最终,我们可以使用迭代器轻松解决原始问题。例如,让我们删除一只名为“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);
结论:

[Cat{name='Томас'}, Cat{name='Бегемот'}, Cat{name='Пушок'}]
您可能已经注意到,我们没有在迭代器方法中指定元素索引或引用变量名称remove()!迭代器比看起来更聪明:该方法remove()删除迭代器返回的最后一个元素。正如您所看到的,它完全按照需要工作:) 这基本上就是您需要了解的有关从ArrayList. 更准确地说 - 几乎一切。在下一堂课中,我们将深入了解该类的“内部”,看看操​​作过程中会发生什么:)再见!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION