CodeGym /Java 博客 /随机的 /从 ArrayList 中删除一个元素
John Squirrels
第 41 级
San Francisco

从 ArrayList 中删除一个元素

已在 随机的 群组中发布
你好!在上一节课中,我们熟悉了这个ArrayList类,并学习了如何用这个类进行最常见的操作。此外,我们指出了数组ArrayList和普通数组之间的几个区别。但是我们绕过了一个话题,即如何ArrayList. 我们现在就来讨论。 从 ArrayList 中删除一个元素 - 1我们已经提到从普通数组中删除元素不是很方便。由于我们无法删除元素本身,我们只能将其值“清零”(设置为 null):

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 + '\'' +
               '}';
   }
}
输出: [Cat{name='Thomas'}, null, Cat{name='Lionel Messi'}] 但是将数组元素设置为 null 会留下一个“洞”。我们没有删除数组中的位置,只有它的内容。想象一下,如果我们有一个包含 50 只猫的数组并以这种方式移除其中的 17 只,会发生什么情况。我们将有一个有 17 个孔的阵列。试着跟踪他们!期望记住可以写入新值的空单元格的数量是不现实的。如果你犯了一个错误,你将覆盖你想要的对象引用。当然,有一种更仔细的方法:删除元素后,元素移动到数组的前面,将“孔”放在末尾:

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));
}
Output: [Cat{name='Thomas'}, Cat{name='Fluffy'}, Cat{name='Fluffy'}, null] 这看起来更好,但很难称为稳健的解决方案。如果不是因为我们每次从数组中删除一个元素时都必须编写这段代码的话!这是一个糟糕的选择。我们可以换一种方式创建一个单独的方法:

public void deleteCat(Cat[] cats, int indexToDelete) {
   //...delete the cat corresponding to the index and move 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("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());
}
我们将对象的索引传递给删除它的方法(就像在数组中一样)。该remove()方法有两个特点。一是不留“洞”。它已经实现了当元素从中间移除时移动元素所需的逻辑,这是我们之前自己编写的。查看前面代码的输出:

[Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]

[Cat{name='Thomas'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}]
我们从中间移走了一只猫,其余的都移动了,这样就没有空位了。 其次,它不仅可以通过索引(如普通数组)删除对象,还可以通过引用删除对象:

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());
}
输出: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Lionel Messi'}, Cat{name='Fluffy'}] [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}] 如果您不想始终跟踪所需对象的索引,这会非常方便。看来我们已经想通了普通的删除。现在让我们想象一下这种情况:我们想要遍历列表并删除具有特定名称的猫。为此,我们将使用快速for循环(也称为 for-each 循环),这是我们在 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);
}
该代码看起来非常合乎逻辑。但结果可能是一个很大的惊喜: 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 Saberhagen"));
}

System.out.println(cats);
java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) 在 java.util.ArrayList$Itr.next(ArrayList.java:831) 在 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 the next element
   System.out.println(nextCat);// Display it
}
输出: Cat{name='Thomas'} Cat{name= ' Behemoth'} Cat{name='Lionel Messi'} Cat{name='Fluffy'} 如您所见,ArrayList迭代器:iterator(). 此外,请注意,当我们创建迭代器时,我们指定了它将使用的对象类 ( <Cat>)。底线是迭代器可以轻松处理我们的原始任务。例如,删除名为“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);
输出: [Cat{name='Thomas'}, Cat{name='Behemoth'}, Cat{name='Fluffy'}]remove()您可能已经注意到我们没有在迭代器的方法 中指定索引或名称!迭代器比它看起来更聪明:remove()删除迭代器返回的最后一个元素。如您所见,它完成了我们希望它完成的工作:) 原则上,这就是您需要了解的有关从ArrayList. 好吧,几乎所有的东西。在下一课中,我们将深入了解这个类,看看在各种方法调用期间那里发生了什么 :) 在那之前!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION