1. 迭代器產生的背景

你已經很熟悉了HashSet。如果你真的研究過它,而不僅僅是閱讀一節課,那麼你應該問這個問題:

如何在屏幕上顯示所有 HashSet 元素的列表?畢竟接口是沒有get()set()方法的!

並且HashSet在這個限制中並不孤單。除了HashSet,還有許多其他集合不允許通過索引檢索元素,因為元素沒有定義的順序。

多年來,程序員發明了許多複雜的數據結構,例如圖和樹。或列表列表。

當添加新元素或刪除現有元素時,許多容器會更改其元素的順序。例如,列表以特定順序存儲元素,當添加新元素時,它幾乎總是插入到列表的中間。

我們還會遇到這樣的情況,即有一個容器存儲元素但沒有以任何固定順序存儲。

現在假設我們要將此類集合中的所有元素複製到數組或列表中。我們需要獲取所有元素。我們不關心迭代元素的順序——重要的是不要多次迭代相同的元素。我們該怎麼做?


2.集合的迭代器

迭代器被提議作為上述問題的解決方案。

迭代器是與集合關聯的特殊對象,它有助於遍歷集合中的所有元素而不重複任何元素。

您可以使用以下代碼獲取任何集合的迭代器:

Iterator<Type> it = name.iterator();

其中name是集合變量的名稱,Type是集合元素的類型,iterator()是集合的方法之一,it是迭代器變量的名稱。

迭代器對像有 3 個方法:

方法 描述
Type next()
返回集合中的下一個元素
boolean hasNext()
檢查是否還有未遍歷的元素
void remove()
移除集合的當前元素

這些方法有點類似於 Scanner 類的nextInt)hasNextInt()方法。

next()方法返回我們從中獲得迭代器的集合的下一個元素。

hasNext()方法檢查集合是否有迭代器尚未返回的其他元素。

以下是顯示 a 的所有元素的方法HashSet

代碼 筆記
HashSet<String> set = new HashSet<String>();

set.add("Hallo");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
創建一個HashSet存儲String元素的對象。


我們將各種語言的問候語添加到set變量中。




獲取集合的迭代器對象set
只要還有元素

獲取下一個元素
將元素顯示在屏幕上


3.For-each循環

迭代器的主要缺點是你的代碼變得比使用for循環更麻煩。

for為了進行比較,讓我們使用循環並使用迭代器來顯示列表:

迭代器 for循環
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

是的,使用循環遍歷 an 的元素要好得多ArrayList——一切都變得更短了。

但是 Java 的創造者再次決定給我們灌些糖。對我們來說幸運的是,它是語法糖

他們為 Java 提供了一種新的循環,並稱之為循環for-each。這是它的一般外觀:

for(Type name:collection)

其中collection是集合變量的名稱,Type是集合中元素的類型,name是在循環的每次迭代中從集合中獲取下一個值的變量的名稱。

這種循環使用隱式迭代器遍歷集合的所有元素。這是它的實際工作方式:

For-each 循環 編譯器看到的:使用迭代器循環
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

當編譯器for-each在您的代碼中遇到循環時,它只是用右邊的代碼替換它:它添加一個調用以獲取迭代器以及任何其他缺少的方法調用。

程序員喜歡for-each循環,並且在需要遍歷集合的所有元素時幾乎總是使用它。

ArrayList即使使用循環遍歷列表for-each看起來也更短:

For-each 循環 for循環
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


for-each4. 刪除循環中的元素

循環for-each有一個缺點:它不能正確地刪除元素。如果你這樣寫代碼,你會得到一個錯誤。

代碼 筆記
ArrayList<String> list = new ArrayList<String>();

list.add("Hallo");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












刪除操作會產生錯誤!

這是一個非常好的和易於理解的代碼,但它不會工作。

重要的!

在使用迭代器遍歷集合時,您不能更改它。

有三種方法可以繞過此限制。

1.使用不同類型的循環

When traversing an ArrayList collection,您可以使用帶有計數器變量的普通循環i

代碼
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--; // We need to decrease i, because the remove operation shifted the elements
   }
}

但是,此選項不適用於HashSetHashMap集合

2.使用顯式迭代器

您可以顯式使用迭代器並調用它的remove()方法。

有效的版本 不起作用的版本
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}

for (String str: list) { if (str.equals("Hello")) list.remove(str); }

請注意,我們remove()在迭代器對像上調用該方法!迭代器知道該項目已被刪除並且可以正確處理這種情況。

3.使用集合的副本

您還可以創建集合的副本,然後在循環中使用副本for-each並從原始集合中刪除元素。

代碼 筆記
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
創建集合的副本非常簡單



循環使用迭代器來創建集合的副本。
元素已從list集合中移除。

集合被複製得相當快,因為元素本身沒有被複製。相反,新集合存儲對舊集合中已存在的元素的引用。