日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

collection集合 多少钱_Java 集合(2)-- Iterator接口源码超级详细解析

發(fā)布時間:2024/10/8 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 collection集合 多少钱_Java 集合(2)-- Iterator接口源码超级详细解析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、iterator接口介紹

iterator接口,也是集合大家庭中的一員。和其他的Map和Collection接口不同,iterator 主要是為了方便遍歷集合中的所有元素,用于迭代訪問集合中的元素,相當(dāng)于定義了遍歷元素的規(guī)范,而另外的Map和Collection接口主要是定義了存儲元素的規(guī)范。
還記得么?之前說的iterable接口,有一個方法就是叫iterator(),也是返回iterator對象。

迭代:不斷訪問集合中元素的方式,取元素之前先判斷是否有元素,有則取出來,沒有則結(jié)束,不斷循環(huán)這個過程,直到遍歷完里面所有的元素。

接口定義的方法如下:

boolean hasNext(); // 是否有下一個元素E next(); // 獲取下一個元素// 移除元素 default void remove() {throw new UnsupportedOperationException("remove");}// 對剩下的所有元素進(jìn)行處理,action則為處理的動作,意為要怎么處理 default void forEachRemaining(Consumer<? super E> action) {Objects.requireNonNull(action);while (hasNext())action.accept(next());}

但是值得注意的是,集合類的整體不是繼承了iterator接口,而是繼承了iterable接口,通過iterable接口的方法返回iterator的對象。值得注意的是,iterator的remove()方法,是迭代過程中唯一安全的修改集合的方法,為何這樣說?
如果使用for循環(huán)索引的方式遍歷,刪除掉一個元素之后,集合的元素個數(shù)已經(jīng)變化,很容易出錯。例如

for(int i=0;i<collection.size();i++){if(i==2){collection.remove(i);} }

而iterator的remove()方法則不會出錯,因?yàn)橥ㄟ^調(diào)用hasNext()和next()方法,對指針控制已經(jīng)處理得比較完善。

二、為什么需要iterator接口

首先,我們知道iterator接口是為了定義遍歷集合的規(guī)范,也是一種抽象,把在不同集合的遍歷方式抽象出來,這樣遍歷的時候,就不需要知道不同集合的內(nèi)部結(jié)構(gòu)。

為什么需要抽象?

假設(shè)沒有iterator接口,我們知道,遍歷的時候只能通過索引,比如

for(int i=0;i<array.size();i++){T item = array[i]; }

這樣一來,耦合程度比較高,如果使用的數(shù)據(jù)結(jié)構(gòu)變了,就要換一種寫法,不利于維護(hù)已有的代碼。如果沒有iterator,那么客戶端需要維護(hù)指針,相當(dāng)于下放了權(quán)限,會造成一定程度的混亂。抽象則是把遍歷功能抽取出來,交給iterator處理,客戶端處理集合的時候,交給更“專業(yè)”的它,it do it well.

三、iterator接口相關(guān)接口

3.1 ListIterator

ListIterator繼承于Iterator接口,功能更強(qiáng)大,只能用于訪問各種List類型,使用List類型的對象list,調(diào)用listIterator()方法可以獲取到一個指向list開頭的ListIterator

從上面圖片接口看,這個接口具有訪問下一個元素,判斷是否有下一個元素,是否有前面一個元素,判斷是否有前一個元素,獲取下一個元素的索引,獲取上一個元素的索引,移除元素,修改元素,增加元素等功能。和普通的Iterator不一樣的是,ListIterator的訪問指針可以向前或者向后移動,也就是雙向移動。

boolean hasNext(); //是否還有元素 E next(); //獲取下一個元素boolean hasPrevious(); //是否有上一個元素E previous(); // 獲取上一個元素int nextIndex(); //獲取下一個索引int previousIndex(); //獲取上一個索引void remove(); //移除void set(E e); //更新void add(E e); //添加元素

測試代碼如下:

List<String> list =new ArrayList<String>(Arrays.asList("Book","Pen","Desk"));// 把指針指向第一個元素ListIterator<String> lit = list.listIterator(1);while(lit.hasNext()){System.out.println(lit.next());}System.out.println("===================================");//指針指向最后一個元素列表中的最后一個元素修改ChangeDesk。lit.set("ChangeDesk");// 往前面遍歷while(lit.hasPrevious()){System.out.println(lit.previous());}

輸出如下:

Pen Desk =================================== ChangeDesk Pen Book

如果點(diǎn)開ArrayList的源碼,看到與ListIterator相關(guān)的部分,我們會發(fā)現(xiàn)其實(shí)ArrayList在底層實(shí)現(xiàn)了一個內(nèi)部類ListItr,繼承了Itr,實(shí)現(xiàn)了ListIterator接口。這個Itr其實(shí)就是實(shí)現(xiàn)了Iterator,實(shí)現(xiàn)了基本的List迭代器功能,而這個ListItr則是增強(qiáng)版的專門為List實(shí)現(xiàn)的迭代器。里面使用cursor作為當(dāng)前的指針(索引),所有函數(shù)功能都是操作這個指針實(shí)現(xiàn)。

private class ListItr extends Itr implements ListIterator<E> {ListItr(int index) {super();// 設(shè)置當(dāng)前指針 cursor = index;}public boolean hasPrevious() {// 不是第一個元素就表明有前一個元素return cursor != 0;}// 獲取下一個元素索引public int nextIndex() {return cursor;}// 獲取前面一個元素索引public int previousIndex() {return cursor - 1;}@SuppressWarnings("unchecked")public E previous() {//檢查是否被修改checkForComodification();int i = cursor - 1;if (i < 0)throw new NoSuchElementException();Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length)throw new ConcurrentModificationException();cursor = i;// 返回前一個元素return (E) elementData[lastRet = i];}public void set(E e) {if (lastRet < 0)throw new IllegalStateException();checkForComodification();try {ArrayList.this.set(lastRet, e);} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}public void add(E e) {checkForComodification();try {int i = cursor;ArrayList.this.add(i, e);cursor = i + 1;lastRet = -1;expectedModCount = modCount;} catch (IndexOutOfBoundsException ex) {throw new ConcurrentModificationException();}}}

我們可以看到,在上面方法中,有很多校驗(yàn),比如checkForComodification(),意為檢查是否被修改,list中的元素修改有可能導(dǎo)致數(shù)組越界。

3.2 SpitIterator

準(zhǔn)確地來說,SpitIterator和Iterator并沒有什么關(guān)系,只是兩個功能上有類似。SpitIterator主要是定義類將集合分割成多個集合,方便并行計算。

3.2.1 SpitIterator源碼方法解析

public interface Spliterator<T> {// 順序處理每一個元素,參數(shù)是處理的動作,如果還有元素需要處理則返回true,否則返回falseboolean tryAdvance(Consumer<? super T> action);// 依次處理剩下的元素default void forEachRemaining(Consumer<? super T> action) {do { } while (tryAdvance(action));}// 最重要的方法,用來分割集合Spliterator<T> trySplit();//估算還有多少元素需要遍歷處理long estimateSize();// 獲取準(zhǔn)確的元素,如果不能獲取準(zhǔn)確的,則會返回估算的default long getExactSizeIfKnown() {return (characteristics() & SIZED) == 0 ? -1L : estimateSize();}// 表示該Spliterator有哪些特性,這個像是個拓展功能,更好控制和優(yōu)化Spliterator使用int characteristics();// 判斷是否有哪些特性default boolean hasCharacteristics(int characteristics) {return (characteristics() & characteristics) == characteristics;}// 如果這個Spliterator的源具有已排序的特征,那么這個方法將返回相應(yīng)的比較器。如果源按自然順序排序,則返回 // null。否則,如果源未排序,則拋出IllegalStateException。default Comparator<? super T> getComparator() {throw new IllegalStateException();}public static final int ORDERED = 0x00000010;public static final int DISTINCT = 0x00000001;public static final int SORTED = 0x00000004;public static final int SIZED = 0x00000040;public static final int NONNULL = 0x00000100;public static final int IMMUTABLE = 0x00000400;public static final int CONCURRENT = 0x00001000;public static final int SUBSIZED = 0x00004000; }

使用的方法例子如下:

public static void spliterator(){List<String> list = Arrays.asList("1", "2", "3","4","5","6","7","8","9","10");// 獲取可迭代器Spliterator<String> spliterator = list.spliterator();// 一個一個遍歷System.out.println("tryAdvance: ");spliterator.tryAdvance(item->System.out.print(item+" "));spliterator.tryAdvance(item->System.out.print(item+" "));System.out.println("n-------------------------------------------");// 依次遍歷剩下的System.out.println("forEachRemaining: ");spliterator.forEachRemaining(item->System.out.print(item+" "));System.out.println("n------------------------------------------");// spliterator1:0~10Spliterator<String> spliterator1 = list.spliterator();// spliterator1:6~10 spliterator2:0~5Spliterator<String> spliterator2 = spliterator1.trySplit();// spliterator1:8~10 spliterator3:6~7Spliterator<String> spliterator3 = spliterator1.trySplit();System.out.println("spliterator1: ");spliterator1.forEachRemaining(item->System.out.print(item+" "));System.out.println("n------------------------------------------");System.out.println("spliterator2: ");spliterator2.forEachRemaining(item->System.out.print(item+" "));System.out.println("n------------------------------------------");System.out.println("spliterator3: ");spliterator3.forEachRemaining(item->System.out.print(item+" "));}
  • tryAdvance() 一個一個元素進(jìn)行遍歷
  • forEachRemaining() 順序地分塊遍歷
  • trySplit()進(jìn)行分區(qū)形成另外的 Spliterator,使用在并行操作中,分出來的是前面一半,就是不斷把前面一部分分出來

結(jié)果如下:

tryAdvance: 1 2 ------------------------------------------- forEachRemaining: 3 4 5 6 7 8 9 10 ------------------------------------------ spliterator1: 8 9 10 ------------------------------------------ spliterator2: 1 2 3 4 5 ------------------------------------------ spliterator3: 6 7

還有一些其他的用法在這里就不列舉了,主要是trySplit()之后,可以用于多線程遍歷。理想的時候,可以平均分成兩半,有利于并行計算,但是不是一定平分的。

3.2.2 SpitIterator里面哪些特征常量有什么用呢?

spliterator可以將其實(shí)現(xiàn)特征表示為同一接口中定義的一組常量。也就是我們見到的ORDERED,DISTINCT,SORTED,SIZED之類的,這個意思是每一個實(shí)現(xiàn)類,都有自己的實(shí)現(xiàn)方式,實(shí)現(xiàn)方式不同,實(shí)現(xiàn)特征也不一樣,比如ArrayList實(shí)現(xiàn)特征是ORDERED,SIZED和SUBSIZED,這個我們可以通過 characteristics() and hasCharacteristics()來判斷。例如:

public static void main(String[] args) throws Exception{List<String> list = new ArrayList<>();Spliterator<String> s = list.spliterator();System.out.println(s.characteristics());if(s.hasCharacteristics(Spliterator.ORDERED)){System.out.println("ORDERED");}if(s.hasCharacteristics(Spliterator.DISTINCT)){System.out.println("DISTINCT");}if(s.hasCharacteristics(Spliterator.SORTED)){System.out.println("SORTED");}if(s.hasCharacteristics(Spliterator.SIZED)){System.out.println("SIZED");}if(s.hasCharacteristics(Spliterator.CONCURRENT)){System.out.println("CONCURRENT");}if(s.hasCharacteristics(Spliterator.IMMUTABLE)){System.out.println("IMMUTABLE");}if(s.hasCharacteristics(Spliterator.NONNULL)){System.out.println("NONNULL");}if(s.hasCharacteristics(Spliterator.SUBSIZED)){System.out.println("SUBSIZED");}}

輸出的結(jié)果是

16464 ORDERED SIZED SUBSIZED

輸出結(jié)果中的16464和其他的怎么掛鉤的呢?其實(shí)我們發(fā)現(xiàn)上面的hasCharacteristics()方法中,實(shí)現(xiàn)是return (characteristics() & characteristics) == characteristics;,不難看出,這些狀態(tài)是根據(jù)與運(yùn)算來計算出來的。上面的結(jié)果也表明ArrayList有ORDERED,SIZED和SUBSIZED這幾個特征。
如果是HashSet則特征是DISTINCT和SIZED。

四、 iterator在集合中的實(shí)現(xiàn)例子

iterator只是一個接口,相當(dāng)于一個規(guī)范,所有的子類或者繼承類實(shí)現(xiàn)的時候理論上應(yīng)該遵守,但是不一樣的繼承類/子類會有不一樣的實(shí)現(xiàn)。

4.1 iterator在ArrayList的實(shí)現(xiàn)

iterator只是一個接口,一個規(guī)范,雖然里面有個別方法有默認(rèn)實(shí)現(xiàn),但是最重要也最豐富的的,是它在子類中的實(shí)現(xiàn)與拓展,現(xiàn)在來看在ArrayList 中的實(shí)現(xiàn)。ArrayList并沒有直接去實(shí)現(xiàn)iterator接口,而是通過內(nèi)部類的方式來操作,內(nèi)部類為Itr,

private class Itr implements Iterator<E> {// 下一個元素的索引(指針)int cursor; // index of next element to return// 最后一個元素指針?biāo)饕齣nt lastRet = -1; // index of last element returned; -1 if no such// 修改次數(shù)(版本號)int expectedModCount = modCount;Itr() {}// 是否有下一個元素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];}// 移除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();}}// 依次處理剩下的元素@Override@SuppressWarnings("unchecked")public void forEachRemaining(Consumer<? super E> consumer) {Objects.requireNonNull(consumer);final int size = ArrayList.this.size;int i = cursor;if (i >= size) {return;}final Object[] elementData = ArrayList.this.elementData;if (i >= elementData.length) {throw new ConcurrentModificationException();}while (i != size && modCount == expectedModCount) {consumer.accept((E) elementData[i++]);}// update once at end of iteration to reduce heap write trafficcursor = i;lastRet = i - 1;checkForComodification();}// 安全檢查,檢查是否被修改final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}}

從上面的源碼可以看到,很多關(guān)于被修改的檢查,集合會追蹤修改(增刪改)的次數(shù)(modCount 又稱版本號),每一個迭代器會單獨(dú)立維護(hù)一個計數(shù)器,在每次操作(增刪改),檢查版本號是否發(fā)生改變,如果改變,就會拋出ConcurrentModificationException() 異常,這是一種安全保護(hù)機(jī)制。
安全檢查,快速失敗機(jī)制實(shí)現(xiàn)主要和變量modCount,expectedModCount,以及一個checkForComodification()方法有關(guān),也就是expectedModCount是內(nèi)部類的修改次數(shù),從字面意思看是指理論上期待的修改次數(shù),modCount是外部類的修改次數(shù),創(chuàng)建的時候,會將modCount賦值給expectedModCount,兩者保持一致,如果在迭代的過程中,外部類的modCount對不上expectedModCount,n那么就會拋出ConcurrentModificationException異常。

4.2 iterator在HashMap的實(shí)現(xiàn)

首先,HashMap里面定義了一個HashIterator,為什么這樣做呢?因?yàn)镠ashMap存儲結(jié)構(gòu)的特殊性,里面有Entry<key,value>,所以遍歷就有三種情況,一個是Key,一個是Value,另一個就是Entry,這三個的迭代遍歷都有相似性,所以這里根據(jù)抽象原則,定義了一個Hash迭代器。

abstract class HashIterator {// 下一個節(jié)點(diǎn)Node<K,V> next;// 當(dāng)前節(jié)點(diǎn)Node<K,V> current; // current entry// 期望修改次數(shù)int expectedModCount; // for fast-fail// 索引int index; // current slotHashIterator() {expectedModCount = modCount;Node<K,V>[] t = table;current = next = null;index = 0;if (t != null && size > 0) { // 指向第一個不為空的元素do {} while (index < t.length && (next = t[index++]) == null);}}// 是否有下一個節(jié)點(diǎn)public final boolean hasNext() {return next != null;}// 獲取下一個節(jié)點(diǎn)final Node<K,V> nextNode() {Node<K,V>[] t;Node<K,V> e = next;if (modCount != expectedModCount)throw new ConcurrentModificationException();if (e == null)throw new NoSuchElementException();if ((next = (current = e).next) == null && (t = table) != null) {do {} while (index < t.length && (next = t[index++]) == null);}return e;}// 移除public final void remove() {Node<K,V> p = current;if (p == null)throw new IllegalStateException();if (modCount != expectedModCount)throw new ConcurrentModificationException();current = null;K key = p.key;removeNode(hash(key), key, null, false, false);expectedModCount = modCount;}}

之后分別定義KeyIterator,ValueIterator,EntryIterator,繼承于HashIterator,

// 遍歷keyfinal class KeyIterator extends HashIteratorimplements Iterator<K> {public final K next() { return nextNode().key; }}// 遍歷valuefinal class ValueIterator extends HashIteratorimplements Iterator<V> {public final V next() { return nextNode().value; }}//遍歷entryfinal class EntryIterator extends HashIteratorimplements Iterator<Map.Entry<K,V>> {public final Map.Entry<K,V> next() { return nextNode(); }}

五、總結(jié)

以上的種種,關(guān)于Iterator,其實(shí)就是一個迭代器,可簡單地理解為遍歷使用,主要功能是指向一個節(jié)點(diǎn),向前或者向后移動,如果數(shù)據(jù)結(jié)構(gòu)復(fù)雜就需要多個迭代器,比如HashMap,可以避免多個迭代器之間相互影響。每一個迭代器都會有 expectedModCount 和modCount,就是校驗(yàn)這個迭代過程中是否被修改,如果修改了,則會拋出異常。

【作者簡介】: 秦懷,公眾號【秦懷雜貨店】作者,技術(shù)之路不在一時,山高水長,縱使緩慢,馳而不息。這個世界希望一切都很快,更快,但是我希望自己能走好每一步,寫好每一篇文章,期待和你們一起交流。

此文章僅代表自己(本菜鳥)學(xué)習(xí)積累記錄,或者學(xué)習(xí)筆記,如有侵權(quán),請聯(lián)系作者核實(shí)刪除。人無完人,文章也一樣,文筆稚嫩,在下不才,勿噴,如果有錯誤之處,還望指出,感激不盡~

- END -

總結(jié)

以上是生活随笔為你收集整理的collection集合 多少钱_Java 集合(2)-- Iterator接口源码超级详细解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。