Enumeration<E>
在說Iterator之前就先說一下被代替的Enumeration<E>
Public interface enumerator<E>{
Public boolean hasMoreElements(); //是否還有元素
Public E nextElement(); //返回下一個元素
}
Enumeration 在1.0的時候就推出了在當時出現的的集合也是用Hashtable Vector也是用它來進行遍歷的
在jdk1.2的時候就推出了Iterator代替他
直到Jdk1.5Enumeration才加入了泛型,但是也沒能拯救他
Iterator(迭代器)和Enumeration(枚舉類)區別
迭代器不僅可以讀取集合中的數據還可以移除數據,而Enumeration只能遍歷
迭代器的名字短
迭代器支持fail-fast機制,而Enumeration不支持
Enumeration本身是不支持同步的,但是在Voctor和Hashtable實現Enumeration時,添加了同步
Iterator是支持fail-fast機制的:當多個線程對同一個集合的內容進行操作時,就可能會產生fail-fast事件。
迭代器Iterator(什麼樣的):
提供一種方法對一個容器對象中的元素進行訪問,而有不暴露該對象容器的內部細節
在java中Itertor是一個接口,只提供了基本的規則
Public interface Iterator<E>{
Boolean hasNext();//判斷是否有系一個元素
E next()//獲取下一個元素
Void remove;//移除元素
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
Java還提供了一個接口Iterable(能夠...樣)
Public interface Iterable<T>{
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) { //foreach循環
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() { //jdk1.8的新迭代器
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
Iterable接口實現後會返回一個迭代器iterator;常用的集合collection類都實現了這個接口.
Spliterator(splitable iterator可分割迭代器)接口是Java爲了並行遍歷數據源中的元素而設計的迭代器,這個可以類比最早Java提供的順序遍歷迭代器Iterator,但一個是順序遍歷,一個是並行遍歷(等什麼時候成爲了dalao在研究這個都是些啥東西啊)
作用
返回一個標準的Iterable實現,實現Iterable接口了就可以通過foreach來遍歷.在編譯時會對foreach進行優化,轉換爲HashNext()和next()的調
爲什麼要在弄兩個接口呢,直接實現Iterator不好嗎
主要是爲了迭代器之間不會相互干擾.
Iterator接口中的核心方法next(),hasNext(),remove(),都是依賴當前位置。如果這些集合直接實現Iterator接口,則勢必導致集合對象中包含當前迭代位置的數據(指針)。當集合在不同方法間進行傳遞的時候,由於當前迭代位置不可知,所以next()的結果也不可知。除非再爲Iterator接口添加一個reset()方法,用來重置當前迭代位置。 而當實現Iterable則不然,每次調用都返回一個從頭開始的迭代器,各個迭代器之間互不影響。
在Iterator進行遍歷的時候不能對正在遍歷的容器進行改變大小的操作
看下Arraylist實現的Iterator
private class Itr implements Iterator<E> {
int cursor; // 表示下一個元素的索引位置
int lastRet = -1; // 表示上一個元素的索引位置
int expectedModCount = modCount;//預期被修改的次數
public boolean hasNext() {
return cursor != size; //當下一個索引的位置不等於元素個數的話 說明還有元素
}
@SuppressWarnings("unchecked")
public E next() { //返回下一個元素
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
fail-fast機制
集合的內部有一個modCount用來記錄集合被修改的次數 而迭代器自己也有一個expectedModCount記錄修改次數
根據最上面的成員屬性 可以知道mod和expected剛開始是相同的 這時候如果遍歷的時候直接用集合的方法修改集合那麼mod和expected就會不同(checkForComodification) 就會拋出concurrxxxxx的異常
而如果是用迭代器自己的remove方法改動 那麼兩個將同時改變就不會報錯
同時有個有趣的現象,ArrayList不用迭代器的方法刪除倒數第二個元素也不是觸發fail-fast機制
注意看那個hasNext方法
在刪除倒數第二個元素的場景下:當倒數第二個元素時迭代完成,開始迭代最後一個元素時,此時cursor是4,size由於在迭代過程倒數第二個元素移除了,所以-1, 此時cursor和size相等,遍歷結束,不會再進入下一個迭代,因此不會觸發checkForComodification方法的fail-fast機制。
ListIterator
感覺Iterator不夠強大,只能移除啊,好多功能沒有
Java又出來一個新的接口,ListIterator接口,繼承Iterator接口
雙向遍歷 可以在遍歷期間修改集合,同時可以獲取迭代器遊標的在集合中的位置
遺憾的是,只有在list其子類才能使用
兩個獲取方式
List.listIterator()
List.listIterator(int location) 可以指定遊標的位置
AbstractList是list的直接子類,就實現了ListIterator()方法,並且有兩個內部迭代器實現類
SimpleListIterator,FullListIterator:
SimpleListIterator 的主要操作最後都交給子類來實現,List 的子類 ArrayList, LinkedList, Vector 由於底層實現原理不同(數組,雙向鏈表),具體操作類實現有所不同。
FullListIterator 繼承了 SimpleListIterator, SimpleListIterator 實現了 Iterator 接口:
Listlterator()返回的fulllistlterator();
ListIterator
Public ListIterator<E> listIterator(int location){
Return new fulllistiterator(location);
}
SimpleListIterator的源碼
private class SimpleListIterator implements Iterator<E> {
//遊標的位置,初始爲 -1
int pos = -1;
//用來判斷是否 fail-fast 的變量
int expectedModCount;
//記錄上次迭代的位置
int lastPosition = -1;
SimpleListIterator() {
expectedModCount = modCount;
}
//當遊標沒有跑到最後一個元素後面時 hasNext 返回 true
public boolean hasNext() {
return pos + 1 < size();
}
//獲取下一個元素
public E next() {
if (expectedModCount == modCount) {
try {
//獲取遊標後面的元素,具體子類有具體實現
E result = get(pos + 1);
//更新
lastPosition = ++pos;
return result;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
//當迭代時修改元素,就會報這個錯
throw new ConcurrentModificationException();
}
//刪除上次迭代操作的元素
public void remove() {
//還沒進行迭代操作就會報這個錯
if (this.lastPosition == -1) {
throw new IllegalStateException();
}
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();
}
try {
//調用子類實現的刪除操作
AbstractList.this.remove(lastPosition);
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
expectedModCount = modCount;
if (pos == lastPosition) {
pos--;
}
//每次刪除後都會還原爲 -1,也就是說我們迭代一次後只能 remove 一次,再 remove 就會報錯
lastPosition = -1;
}
}
//FullListIterator源碼
private final class FullListIterator extends SimpleListIterator implements ListIterator<E> {
//根據 start 指定遊標位置
FullListIterator(int start) {
if (start >= 0 && start <= size()) {
pos = start - 1;
} else {
throw new IndexOutOfBoundsException();
}
}
//在遊標前面添加元素
public void add(E object) {
if (expectedModCount == modCount) {
try {
//調用子類的添加操作,ArrayList, LinkedList,Vector 的添加操作實現有所不同
AbstractList.this.add(pos + 1, object);
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
//遊標後移一位
pos++;
//!注意! 添加後 上次迭代位置又變回 -1 了,說明 add 後調用 remove, set 會有問題!
lastPosition = -1;
if (modCount != expectedModCount) {
expectedModCount = modCount;
}
} else {
throw new ConcurrentModificationException();
}
}
//當遊標不在初始位置(-1)時返回true
public boolean hasPrevious() {
return pos >= 0;
}
//遊標後面的元素索引,就是遊標 +1
public int nextIndex() {
return pos + 1;
}
//遊標前面一個元素
public E previous() {
if (expectedModCount == modCount) {
try {
E result = get(pos);
lastPosition = pos;
pos--;
return result;
} catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException();
}
}
throw new ConcurrentModificationException();
}
//遊標前面元素的索引
public int previousIndex() {
return pos;
}
//更新之前迭代的元素爲 object
public void set(E object) {
if (expectedModCount == modCount) {
try {
//調用子類的set
AbstractList.this.set(lastPosition, object);
} catch (IndexOutOfBoundsException e) {
throw new IllegalStateException();
}
} else {
throw new ConcurrentModificationException();
}
}
}