JavaRush /Java Blog /Random-TW /在 Java 中從 ArrayList 中刪除元素

在 Java 中從 ArrayList 中刪除元素

在 Random-TW 群組發布
你好!在上一講中,我們熟悉了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