說在前面
人生的大道上默默地走,就必須要有一盞燈亮著為你引導(dǎo)方向!而這盞燈抑或只是一句話,一句鼓勵,一個贊美,一次承認(rèn),一次認(rèn)可,一次相識一次交流……
上篇文章:阿里JAVA開發(fā)手冊零度的思考理解(一)得到作者孤盡的肯定支持,那是一個小激動啊,我會繼續(xù)努力,繼續(xù)閱讀和思考阿里JAVA開發(fā)手冊,畢竟每一條都是前人踩過的坑,通過血的教訓(xùn)總結(jié)出來的。
上篇題目回顧
阿里JAVA開發(fā)手冊
看完這條,個人覺得主要是集合相關(guān)操作,在JAVA基礎(chǔ)中集合這塊的重要性也的確非常重要(畢竟是用到最多的),本期只會結(jié)合上題進(jìn)行一些簡單擴(kuò)展,并不會涵蓋所有集合操作,也不涉及集合是否線程安全這塊,后期我會在我的系列高并發(fā)、鎖系列里擴(kuò)展深入。
集合的重要性
已經(jīng)有數(shù)組了為什么會出現(xiàn)集合呢?依然清晰的記得數(shù)據(jù)結(jié)構(gòu)里面順序結(jié)構(gòu)、鏈?zhǔn)浇Y(jié)構(gòu)的特點(diǎn)。在這里數(shù)組就屬于順序結(jié)構(gòu)(但是集合里面根據(jù)順序結(jié)構(gòu)或者鏈?zhǔn)浇Y(jié)構(gòu)實(shí)現(xiàn)的都有,所以在選擇用那個的時候最起碼需要有那么一點(diǎn)點(diǎn)思考而不是拿什么用什么)。
數(shù)組一旦定義,長度將不能再變化。并且數(shù)組僅僅是一個一連串的變量而已,對于很多重復(fù)的操作(并沒有進(jìn)行統(tǒng)一的抽象)而且有些順序結(jié)構(gòu)并不太適合,需要鏈?zhǔn)浇Y(jié)構(gòu)實(shí)現(xiàn)適合或者是需要順序結(jié)構(gòu)與鏈?zhǔn)浇Y(jié)構(gòu)結(jié)合實(shí)現(xiàn)才比較合適。
備注:對于很多重復(fù)的操作,比如如果需要擴(kuò)容,需要自己實(shí)現(xiàn),根據(jù)編碼水平不同實(shí)現(xiàn)的效率不一樣(而且這個可能大量存在,每個人都需要實(shí)現(xiàn),不符合工程學(xué)的思想),再比如需要排序,增刪,遍歷等等。
上面的一些問題就引入了集合并且解決了這些問題,所以集合非常重要,并且項(xiàng)目中集合到處可見,需要把db,nosql里面的數(shù)據(jù)接收下來。
下面看看集合具備的幾個特性 :
這種框架是高性能的,對于基本集合(動態(tài)數(shù)組、鏈表、樹和散列表)的實(shí)現(xiàn)是高效的,并且是經(jīng)過高度測試(不管是性能,安全等都是很可靠的)。
集合允許不同類型的集合以相同的方式繼續(xù)操作。
集合是容易擴(kuò)展和修改的。
集合的遍歷思考
集合遍歷,從工程學(xué)我們需要提供一種方法順序訪問一個集合對象中的各各元素,而又不需要暴露該對象的內(nèi)部表示。
如何才能做到呢??? 迭代器模式就可以做到,下面帶大家一起去了解下。
迭代器模式
迭代器模式的功能主要在于提供對聚合對象的迭代訪問。主要就是這個訪問進(jìn)行做文章的。那么為什么使用迭代器模式呢?有什么好處呢?
集合對象的類型很多,如果對集合對象的迭代訪問跟集合對象本身融合在一起的話,會嚴(yán)重影響到集合對象的可擴(kuò)展性和可維護(hù)性。
備注:迭代器模式的關(guān)鍵思想就是把對集合對象的遍歷和訪問從集合對象中分離出來,放到單獨(dú)的迭代器中,這樣集合對象會變得簡單一些;而迭代器和集合對象可以獨(dú)立的變化和發(fā)展,這樣就大大增強(qiáng)類系統(tǒng)的靈活性。
一般情況下面,使用的都是外部迭代器(由客戶端來控制迭代器的下一個元素的步驟,就是在代碼里面我們需要手動調(diào)用next來迭代下一個元素,這樣做就是要靈活點(diǎn))
JDK5之后引入的新特性foreach( 增強(qiáng)版for)
備注:通過使用javap查看反編譯代碼,在數(shù)組里面,是固有的foreach實(shí)現(xiàn),直接循環(huán)數(shù)組,而在容器的迭代foreach是通過迭代器來實(shí)現(xiàn)。
數(shù)組foreach
容器的迭代foreach
在稍微多做點(diǎn)鋪墊
ArrayList對Iterator接口實(shí)現(xiàn)
備注:ArrayList里面對Iterator實(shí)現(xiàn)了2種,一種是普通的從前向后,而第二種是雙向迭代輸出,可以從往前也可以往后。
解題
阿里JAVA開發(fā)手冊
上面說了那么多,我覺得現(xiàn)在可以開始解題了,各位看官久等了。并發(fā)系列又是另外一個重要的話題,先不考慮并發(fā)進(jìn)行分析,如果并發(fā)操作,需要對Iterator對象加鎖,這個應(yīng)該好理解。
This field is used by the iterator and list iterator implementation returned by the iterator and listIterator methods. If the value of this field changes unexpectedly, the iterator (or list iterator)will throw a ConcurrentModificationException in response to the next, remove, previous, set or add operations.This provides fail-fast behavior, rather than non-deterministic behavior in the face of concurrent modification during iteration.
Use of this field by subclasses is optional. If a subclass wishes to provide fail-fast iterators (and list iterators), then it merely has to increment this field in itsadd(int, E) and remove(int)methods (and any other methods that it overrides that result in structural modifications to the list). A single call to add(int, E) or remove(int) must add no more than one to this field, or the iterators (and list iterators) will throw bogus ConcurrentModificationExceptions. If an implementation does not wish to provide fail-fast iterators, this field may be ignored.
所以應(yīng)該注意,并不僅僅包括remove,add元素也請使用Iterator方式。
這一條標(biāo)準(zhǔn)是加了強(qiáng)制的,說明了重要性,按照上面的優(yōu)秀實(shí)踐去做就對了。
publicstaticvoidmain(String[] args){? ? ? ? List list =newArrayList();? ? ? ? ? ? ? ? list.add("1");? ? ? ? list.add("2");for(String item:list){if("1".equals(item)){//(1? ? 換成? if("2".equals(item)){list.remove(item);? ? ? ? ? ? }? ? ? ? }? ? }
當(dāng)(1? 換成? if("2".equals(item)){? 之后,運(yùn)行結(jié)果報異常,結(jié)果如圖:
結(jié)果
其實(shí)這種給出了錯誤,并且有代碼行數(shù)的情況其實(shí)發(fā)現(xiàn)查找問題都挺方便的,其實(shí)該問題的重點(diǎn)就變成了都是基于Iterator的輸出,但是在進(jìn)行刪除元素的時候應(yīng)該用那種方式才正確。
沒有必要糾結(jié)為什么1不錯,而2錯,稍微看下源碼就知道了,其實(shí)我們也可以讓2不錯,只是jdk里面就是這樣實(shí)現(xiàn)的,它的解釋和考慮如下原因。
原因
ArrayList此類的 iterator 和 listIterator 方法返回的迭代器是快速失敗的:在創(chuàng)建迭代器之后,除非通過迭代器自身的 remove 或 add 方法從結(jié)構(gòu)上對列表進(jìn)行修改,否則在任何時間以任何方式對列表進(jìn)行修改,迭代器都會拋出 ConcurrentModificationException。因此,面對并發(fā)的修改,迭代器很快就會完全失敗,而不是冒著在將來某個不確定時間發(fā)生任意不確定行為的風(fēng)險。
快速失敗
所以最佳實(shí)踐就按照阿里java開發(fā)手冊里面那樣就好了,add元素也請使用Iterator方式。
實(shí)際工作中迭代器用法
可能說完,大家感覺迭代器就僅僅在集合遍歷里面用,而且都已經(jīng)有了,其實(shí)實(shí)際中的確有一些用法,反正都是圍繞控制訪問的,比如分頁,非常常見的情況,如果每次都基于數(shù)據(jù)庫分頁那么怕性能不好,如果完全在內(nèi)存(內(nèi)存太貴,數(shù)據(jù)太多,不現(xiàn)實(shí)),一般的做法就是比如一頁20條數(shù)據(jù),我們一般可以每次查詢數(shù)據(jù)庫的時候取5頁到內(nèi)存(具體每次取多少可以根據(jù)用戶行為分析,得到一個比較合理的,而且越到后面訪問的機(jī)會越少,取到內(nèi)存的就越少了,可以先比如每次都是取n頁數(shù)據(jù),在多少頁之后每次取m頁 之后在每次取一頁一頁了。n>m>1)。那么比如取出來的100條數(shù)據(jù)在內(nèi)存中,需要進(jìn)行根據(jù)分頁訪問,而原來的jdk里面的好像不滿足,那么自己實(shí)現(xiàn)一個類似的是不是特別靈活呢?后續(xù)有空,我會在我的微信公眾號,系列文章的技術(shù)思考里面把類似這塊分析下的。
思考
阿里JAVA開發(fā)手冊
作者:匠心零度
鏈接:http://www.lxweimin.com/p/f2355c17ca28
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。