一、Iterator介紹
Java Iterator(迭代器)不是一個集合,它是一種用於訪問集合的方法,可用於迭代 ArrayList 和 HashSet 等集合。
Iterator 是 Java 迭代器最簡單的實現,ListIterator 是 Collection API 中的接口, 它擴展了 Iterator 接口。
Iterator 類圖
迭代器 it 的兩個基本操作是 next 、hasNext 和 remove。
調用 it.next() 會返回迭代器的下一個元素,並且更新迭代器的狀態。
調用 it.hasNext() 用於檢測集合中是否還有元素。
調用 it.remove() 將迭代器返回的元素刪除。
Iterator 類位於 java.util 包中,使用前需要引入它,語法格式如下:
1 import java.util.Iterator; // 引入 Iterator 類
二、Iterator使用
1、獲取一個迭代器
集合想獲取一個迭代器可以使用 iterator() 方法:
1 // 引入 ArrayList 和 Iterator 類 2 import java.util.ArrayList; 3 import java.util.Iterator; 4 5 public class RunoobTest { 6 public static void main(String[] args) { 7 8 // 創建集合 9 ArrayList<String> sites = new ArrayList<String>(); 10 sites.add("Google"); 11 sites.add("Runoob"); 12 sites.add("Taobao"); 13 sites.add("Zhihu"); 14 15 // 獲取迭代器 16 Iterator<String> it = sites.iterator(); 17 18 // 輸出集合中的第一個元素 19 System.out.println(it.next()); 20 } 21 }
2、循環集合元素
讓迭代器 it 逐個返回集合中所有元素最簡單的方法是使用 while 循環:
1 while(it.hasNext()) { 2 System.out.println(it.next()); 3 }
以下輸出集合 sites 中的所有元素:
1 // 引入 ArrayList 和 Iterator 類 2 import java.util.ArrayList; 3 import java.util.Iterator; 4 5 public class RunoobTest { 6 public static void main(String[] args) { 7 8 // 創建集合 9 ArrayList<String> sites = new ArrayList<String>(); 10 sites.add("Google"); 11 sites.add("Runoob"); 12 sites.add("Taobao"); 13 sites.add("Zhihu"); 14 15 // 獲取迭代器 16 Iterator<String> it = sites.iterator(); 17 18 // 輸出集合中的所有元素 19 while(it.hasNext()) { 20 System.out.println(it.next()); 21 } 22 } 23 }
3、刪除元素
要刪除集合中的元素可以使用 remove() 方法。
以下實例我們刪除集合中小於 10 的元素:
1 // 引入 ArrayList 和 Iterator 類 2 import java.util.ArrayList; 3 import java.util.Iterator; 4 5 public class RunoobTest { 6 public static void main(String[] args) { 7 ArrayList<Integer> numbers = new ArrayList<Integer>(); 8 numbers.add(12); 9 numbers.add(8); 10 numbers.add(2); 11 numbers.add(23); 12 Iterator<Integer> it = numbers.iterator(); 13 while(it.hasNext()) { 14 Integer i = it.next(); 15 if(i < 10) { 16 it.remove(); // 刪除小於 10 的元素 17 } 18 } 19 System.out.println(numbers); 20 } 21 }
二、ArrayList中Iterator實現原理
最好先弄懂ArrayList實現原理:【Java】ArrayList 的實現原理
1、ArrayList 中 Interator 創建
如下:
1 public Iterator<E> iterator() { 2 return new Itr(); 3 }
它是創建了一個 ArrayList.Itr 對象
1 private class Itr implements Iterator<E> { 2 // 游標,默認為0,是下一個要返回元素的標 3 int cursor; // index of next element to return 4 // 最后返回元素的下標,默認為-1 5 int lastRet = -1; // index of last element returned; -1 if no such 6 // 期望的集合修改次數,默認為集合的修改次數 7 int expectedModCount = modCount; 8 9 10 Itr() {} 11 12 ...... 13 }
2、 Interator 的 hasNext() 方法
1 public boolean hasNext() { 2 // 游標不等於集合大小 3 return cursor != size; 4 }
3、 Interator 的 next() 方法
1 // 返回下一個元素 2 public E next() { 3 // 檢查集合是否被修改 4 //同個期望的集合修改次數 是否相同比較,這里也就是集合fastfail機制 5 checkForComodification(); 6 // 需要返回元素的下標就是 cursor的值 7 int i = cursor; 8 if (i >= size) 9 throw new NoSuchElementException(); 10 Object[] elementData = ArrayList.this.elementData; 11 if (i >= elementData.length) 12 throw new ConcurrentModificationException(); 13 // 游標 + 1 14 cursor = i + 1; 15 // 給 lastRet 賦值,並且返回對應元素 16 return (E) elementData[lastRet = i]; 17 }
4、 Interator 的 remove() 方法
1 // 移除一個當前迭代元素 2 public void remove() { 3 if (lastRet < 0) 4 throw new IllegalStateException(); 5 // 檢查集合是否被修改 6 checkForComodification(); 7 8 try { 9 // 調用集合對象的remove方法 10 ArrayList.this.remove(lastRet); 11 // 修改游標,指向當前位置 12 cursor = lastRet; 13 // 最后返回元素的下標,設為-1 14 lastRet = -1; 15 // 重新獲取集合修改次數 16 expectedModCount = modCount; 17 } catch (IndexOutOfBoundsException ex) { 18 throw new ConcurrentModificationException(); 19 } 20 }
5、 Interator 的checkForComodification() 方法
1 // 檢查集合是否被修改 2 // 同個期望的集合修改次數 是否相同比較,這里也就是集合fastfail機制 3 final void checkForComodification() { 4 if (modCount != expectedModCount) 5 throw new ConcurrentModificationException(); 6 }
期望的集合修改次數 是否被修改,這里也就是集合fast-fail機制
在ArrayList介紹中有這么一段話:
注意,迭代器的快速失敗行為無法得到保證,因為一般來說,不可能對是否出現不同步並發修改做出任何硬性保證。快速失敗迭代器會盡最大努力拋出 ConcurrentModificationException。因此,為提高這類迭代器的正確性而編寫一個依賴於此異常的程序是錯誤的做法:迭代器的快速失敗行為應該僅用於檢測 bug。
三、HashSet(HashMap)中Iterator實現原理
最好先弄懂HashMap實現原理:【Java】HashMap 的實現原理
1、HasSet 中 Interator 創建
1 // 創建iterator 2 public Iterator<E> iterator() { 3 // 使用hashmap中的keySet方法返回的keySet集合對象 4 // 通過keySet集合對象調用iterator(),返回迭代器 5 return map.keySet().iterator(); 6 }
由於HashSet底層使用的是HashMap,所有map.keySet(),返回的是hashMap對象的Set集合,如下:
1 // keySet() 方法 2 public Set<K> keySet() { 3 Set<K> ks = keySet; 4 if (ks == null) { 5 // 第一調用創建會去新建一個KeySet對象 6 ks = new KeySet(); 7 keySet = ks; 8 } 9 return ks; 10 }
而KeySet對象,的iterator() 方法,每次調用都會創建一個KeyIterator對象
1 final class KeySet extends AbstractSet<K> { 2 ....... 3 4 // 新建一個KeyIterator對象返回 5 public final Iterator<K> iterator() { return new KeyIterator(); } 6 7 ....... 8 }
KeyIterator對象繼承了HashIterator
1 final class KeyIterator extends HashIterator 2 implements Iterator<K> { 3 public final K next() { return nextNode().key; } 4 }
查看HashIterator,方向迭代器基本實現在這里
1 abstract class HashIterator { 2 // 下一個要返回的節點 3 Node<K,V> next; // next entry to return 4 // 當前節點 5 Node<K,V> current; // current entry 6 // 期望集合修改次數 7 int expectedModCount; // for fast-fail 8 // 在數組中的當前槽位 9 int index; // current slot 10 11 // 構造方法 12 HashIterator() { 13 expectedModCount = modCount; 14 Node<K,V>[] t = table; 15 current = next = null; 16 index = 0; 17 // 當數組不空 且 集合大小大於0 18 if (t != null && size > 0) { // advance to first entry 19 // 找到第一個有數據的槽位 20 do {} while (index < t.length && (next = t[index++]) == null); 21 } 22 } 23 ....... 24 }
2、 Interator 的 hasNext() 方法
1 // 是否還有下一個元素 2 public final boolean hasNext() { 3 return next != null; 4 }
3、 Interator 的 next() 方法
1 // 返回下一個元素 2 public final K next() { return nextNode().key; } 3 4 // 下一個節點 5 final Node<K,V> nextNode() { 6 Node<K,V>[] t; 7 // e 指向 next 8 Node<K,V> e = next; 9 // 檢查集合是否被修改 10 //同個期望的集合修改次數 是否相同比較,這里也就是集合fastfail機制 11 if (modCount != expectedModCount) 12 throw new ConcurrentModificationException(); 13 if (e == null) 14 throw new NoSuchElementException(); 15 // 判斷下一個元素是否為空 16 if ((next = (current = e).next) == null && (t = table) != null) { 17 // 在數組中的當前槽位 + 1 18 // 找到下一個有數據的槽位 19 do {} while (index < t.length && (next = t[index++]) == null); 20 } 21 return e; 22 }
4、 Interator 的 remove() 方法
1 // 移除一個當前迭代元素 2 public final void remove() { 3 Node<K,V> p = current; 4 if (p == null) 5 throw new IllegalStateException(); 6 // 檢查集合是否被修改 7 if (modCount != expectedModCount) 8 throw new ConcurrentModificationException(); 9 current = null; 10 K key = p.key; 11 // 調用hashMap中的removeNode 方法,移除元素 12 removeNode(hash(key), key, null, false, false); 13 // 重新獲取集合修改次數 14 expectedModCount = modCount; 15 }
modCount集合修改次數,這里也就是集合fast-fail機制
在HashMap介紹中有這么一段話:
注意,迭代器的快速失敗行為不能得到保證,一般來說,存在非同步的並發修改時,不可能作出任何堅決的保證。快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴於此異常的程序的做法是錯誤的,正確做法是:迭代器的快速失敗行為應該僅用於檢測程序錯誤。
fast-fail機制解決方案
造成fast-fail 原因,基本是多線程操作集合引起的,解決fast-fail問題,即使解決集合安全問題
1、方案一:集合安全問題,可以使用synchronzied關鍵字
2、方案二:使用Collections.synchronizedList()、Collections.synchronizedMap()
3、方案三:使用其他安全集合,比如:CopyOnWriteArrayList、ConcurrentHashMap
參考:https://www.runoob.com/java/java-iterator.html