Iterable和Iterator
Iterator
接口位于的位置是java.util.Iterator
,它主要有兩個抽象方法供子類實現。hasNext()
用來判斷還有沒有數據可供訪問,next()
用來訪問下一個數據。
集合Collection
不是直接去實現Iterator
接口,而是去實現Iterable
接口。
用這個Iterable
接口的iterator()
方法返回當前集合迭代器。
集合Collection
體系的集合都得按照使用iterator()
的方式返回可供遍歷的迭代器iterator
。
集合使用Iterable
接口的iterator()
方法返回迭代器Iterator
有很多好處。
比如有兩個線程都需要使用迭代器進行遍歷集合的操作,如果通過直接實現Iterator
接口來拿迭代器。首先兩者拿的是同一個迭代器,并且兩者不同步,一個都遍歷結束了,另一個都還沒開始就無法遍歷集合了,但是他本來循環從頭開始遍歷集合的所有數據的。
如果使用Iterable
接口的iterator()
方法返回迭代器Iterator
,那兩者獲得的就是不同迭代器了,就互不影響,自己可以按照自己的進度遍歷集合就行。
for-each就是迭代器的語法糖
要使用增強型 for 循環(也稱為 for-each 循環)遍歷一個集合,集合的類需要實現 java.lang.Iterable
接口。Iterable 接口定義了一個方法 iterator(),它返回一個 Iterator 對象,用于遍歷集合的元素。
import java.util.Iterator;
import java.util.NoSuchElementException;// Iterable位于java.lang包下,不用顯式import
public class MyCollection<T> implements Iterable<T> {private T[] elements;private int size;@SuppressWarnings("unchecked")public MyCollection(int capacity) {elements = (T[]) new Object[capacity];size = 0;}public void add(T element) {if (size < elements.length) {elements[size++] = element;} else {throw new IllegalStateException("Collection is full");}}// 當一個類要實現 Iterable 接口,它必須提供一個 iterator() 方法,該方法返回一個 Iterator 對象。@Overridepublic Iterator<T> iterator() {return new MyIterator();}private class MyIterator implements Iterator<T> {private int currentIndex = 0;@Overridepublic boolean hasNext() {return currentIndex < size;}@Overridepublic T next() {if (!hasNext()) {throw new NoSuchElementException();}return elements[currentIndex++];}}
}
MyCollection<String> collection = new MyCollection<>(10);collection.add("a");collection.add("b");collection.add("c");// 使用增強型 for 循環for (String element : collection) {System.out.println(element);}// 和增強for循環等價的顯式迭代器循環// 當一個類實現了 Iterable 接口,它必須提供一個 iterator() 方法,該方法返回一個 Iterator 對象。// 這個 Iterator 對象實現了 hasNext() 和 next() 方法,用于遍歷集合中的元素。// 下面的 iterator 是通過集合類實現的 Iterable 接口的 iterator() 方法獲得的Iterator<String> iterator = collection.iterator();while (iterator.hasNext()) {String element = iterator.next();System.out.println(element);
快速失敗機制的工作原理
fail-fast 機制是java集合(Collection)中的一種錯誤機制。當多個線程對同一個集合的內容進行操作時,就可能會產生fail-fast事件。
Java中的集合類(如ArrayList、LinkedList、HashMap等)使用快速失敗機制來檢測在迭代過程中對集合的結構性修改。
工作原理如下:
- modCount:每個集合類都有一個modCount字段,用于記錄集合結構性修改的次數。結構性修改是指添加或刪除元素,改變集合的大小。
- 迭代器的expectedModCount:當創建迭代器時,迭代器會保存當前集合的modCount值到一個名為expectedModCount的字段中。
- 一致性檢查:在每次調用迭代器的
next()
或hasNext()
方法時,迭代器首先會檢查modCount是否與expectedModCount相同。如果不相同,說明集合在迭代過程中的結構被修改過,迭代器會拋出ConcurrentModificationException
總的來說:只要是涉及了改變ArrayList元素的個數的方法都會導致modCount的改變。當多線程環境下,由于expectedModCount與modCount的改變不同步,導致兩者之間不等,從而產生fast-fail。
這里的拋出異常,停止執行就是fail-fast,就是當自己遇到無法處理的情況時的處理方式。
下面是ConcurrentModificationException
異常的例子:
List<String> list = new ArrayList<>();
list.add("a");
Iterator<String> iterator = list.iterator(); // expectedModCount = modCount = 0
list.add("b"); // modCount = 1
iterator.next(); // expectedModCount != modCount, 拋出ConcurrentModificationException異常
for-each循環對集合進行增刪也可能拋出異常,因為for-each在反編譯下可以發現就是迭代器的語法糖,所以涉及到對迭代器的使用。
List<String> collection = new ArrayList<>();collection.add("a");collection.add("b");// 使用增強型 for 循環for (String element : collection) {System.out.println(element);}// 和增強for循環等價的顯式迭代器循環// 當一個類實現了 Iterable 接口,它必須提供一個 iterator() 方法,該方法返回一個 Iterator 對象。// 這個 Iterator 對象實現了 hasNext() 和 next() 方法,用于遍歷集合中的元素。// 下面的 iterator 是通過集合類實現的 Iterable 接口的 iterator() 方法獲得的Iterator<String> iterator = collection.iterator();while (iterator.hasNext()) {String element = iterator.next();if ("a".equals(element)) {collection.remove(element);}}
想要在循環時進行增刪操作,也就是進行這類對集合結構性有影響的操作,就要保證數據隔離性,下面是三種數據隔離性的處理方式,本質都是復制東西,只是復制的東西有所不同。
寫入時復制(copy-on-write, 簡稱COW)。
GC 代表 “Garbage Collection”(垃圾回收)。COW讀操作時并沒有加鎖,這是為了提高讀操作的性能,但是有缺點,比如讀數據的時候可能讀不到最新的數據。例如,線程1往集合里面add數據才增加了一半,線程2這時候就去讀數據,那讀到的就還是老數據。
這樣的話就只有增刪才需要開辟一個新數組,其他情況都是使用原數組引用來讀取原數組。
// 可以像使用普通的 ArrayList 一樣使用 CopyOnWriteArrayList(寫入時復制),并且可以通過 List 接口來引用它List<String> cowList = new CopyOnWriteArrayList<>();cowList.add("a");cowList.add("b");cowList.add("c");Iterator<String> iterator = cowList.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}cowList.remove("b");Iterator<String> iterator2 = cowList.iterator();while (iterator2.hasNext()) {System.out.println(iterator2.next());}