版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
ListMapCollectionListListList使用Arrays.asList把數(shù)據(jù)轉(zhuǎn)換為L(zhǎng)istJava8中Stream流式處理的各種功能,大大減少了集合類(投影、過濾、轉(zhuǎn)換)Lis繼續(xù)展開各種Stream操作。你可能也想到了,使用Arrays.asList方法可以把數(shù)組一鍵轉(zhuǎn)換為L(zhǎng)ist,但其實(shí)沒這么簡(jiǎn)Arrays.asListList代代1int[]arr={1,2,Listlist=("list:{}size:{}class:{}",list,list.size(),List3List。通過日志可以發(fā)現(xiàn),這個(gè)List包含的其實(shí)是一個(gè)int數(shù)組,整個(gè)List的元素個(gè)數(shù)是1,元素類型是整數(shù)數(shù)組。1112:50:39.445[main]INFO代其原因是,只能是把int裝箱為Integer,不可能把int數(shù)組裝箱為Integer數(shù)組。我們知道,Arrays.asList方法傳入的是一個(gè)泛型T類型可變參數(shù),最終int數(shù)組整體作為了一個(gè)對(duì)象成為了泛型類型T:1123publicstatic<T>List<T>asList(T...{returnnew}代ListBugJava8用Arrays.stream方法來轉(zhuǎn)換,否則可以把int數(shù)組為包裝類型Integer數(shù)組:代代12345678int[]arr1={1,2,Listlist1=Arrays.stream(arr1).boxed().collect(Collectors.toList());("list:{}size:{}class:{}",list1,list1.size(),list1.get(0).getClasInteger[]arr2={1,2,Listlist2=("list:{}size:{}class:{}",list2,list2.size(),List1113:10:57.373[main]INFO代可以看到第一個(gè)坑是,Arrays.asList。那么,我們獲得了正確的List,是不是就可以像普通的List那樣使用了呢?我們繼續(xù)往下看。把三個(gè)字符串1、2、3構(gòu)成的字符串?dāng)?shù)組,使用Arrays.asList轉(zhuǎn)換為L(zhǎng)ist后,將原始字符串?dāng)?shù)組的第二個(gè)字符修改為4,然后為L(zhǎng)ist增加一個(gè)字符串5,最后數(shù)組和List會(huì)是怎代代1String[]arr={"1","2",Listlist=arr[1]=try}catch(Exceptionex)899("arr:{}list:{}",Arrays.toString(arr),代代123atat613:15:34.699[main]INFO45第二個(gè)坑,Arrays.asList返回的List不支持增刪操作。Arrays.asList返回的List并不是我們期望的java.util.ArrayList,而是Arrays的內(nèi)部類ArrayList。ArrayList內(nèi)部類繼承自Listaddadd代代123456789publicstatic<T>List<T>asList(T...{returnnew}privatestaticclassArrayList<E>extendsimplementsRandomAccess,java.io.Serializable{privatefinalE[]ArrayList(E[]array)a=}publicEset(intindex,E{EoldValue=a[index];a[index]=element;return}}List<E>Collection<E>publicvoidadd(intindex,Eelement)thrownew}}第三個(gè)坑,ListArrayList以發(fā)現(xiàn)ArrayList其實(shí)是直接使用了原始的數(shù)組。所以,我們要特別,把通過Arrays.asList獲得的ListBugnew一個(gè)ArrayList初始化Arrays.asList返回的List代代1String[]arr={"1","2",Listlist=newarr[1]=try}catch(Exceptionex)89("arr:{}list:{}",Arrays.toString(arr),List正的ArrayList,add也不再出錯(cuò):1113:34:50.829[main]INFO代使用List.subList進(jìn)行切片操作居然會(huì)導(dǎo)致ListList,我們通常會(huì)想到使用List.subList方法。但,和Arrays.asList的問題類似,List.subList返回的子tArrLttt的視圖,會(huì)和原始Lt相互影響。如果不注意,很可能會(huì)因此產(chǎn)生OOM問題。接下來,我們就一起分析下其中的如下代碼所示,定義一個(gè)名為data的靜態(tài)List來存放Integer的List,也就是說data的成員本身是包含了多個(gè)數(shù)字的List。循環(huán)1000次,每次都從一個(gè)具有10萬(wàn)個(gè)Integer的List中,使用subList方法獲得一個(gè)只包含一個(gè)數(shù)字的子List,并把這個(gè)子List加入data代代12345678privatestaticList<List<Integer>>data=newprivatestaticvoidoom()for(inti=0;i<1000;i++)List<Integer>rawList=IntStream.rangeClosed(1,100000).boxed().colledata.add(rawList.subList(0,1));}}你可能會(huì)覺得,這個(gè)data變量里面最終保存的只是1000個(gè)具有1個(gè)元素的List,不會(huì)占用很大空間,但程序運(yùn)行不久就出現(xiàn)了OOM:代代Exceptioninthread"main"java.lang.OutOfMemoryError:JavaheapatatOOM100010List它始終被subList方法返回的List強(qiáng)。那么,返回的子List為什么會(huì)強(qiáng)原始的List,它們又有什么關(guān)系呢?我們?cè)倮^續(xù)做實(shí)驗(yàn)觀察一下這個(gè)子List的特性。110ArrayListsubList2、3、4;隨后刪除這個(gè)SubList中的元素?cái)?shù)字3,并打印原始的ArrayList;最后為原始的ArrayList增加一個(gè)元素?cái)?shù)字0,遍歷SubList輸出所有元素:代代1List<Integer>list=IntStream.rangeClosed(1,List<Integer>List<Integer>subList=list.subList(1,try}catch(Exceptionex)11代代12345678[2,3,[1,2,4,5,6,7,8,9,原始List中數(shù)字3ListList0List,會(huì)出現(xiàn)ArrayList//overflow-consciousif(minCapacity-elementData.length> publicvoidadd(intindex,Eelement)ensureCapacityInternal(size+//IncrementsSystem.arraycopy(elementData,index,elementData,index+size-elementData[index]=}publicList<E>subList(intfromIndex,inttoIndex)subListRangeCheck(fromIndex,toIndex,returnnewSubList(this,offset,fromIndex, privateclassSubList List<E>implementsRandomAccessprivate List<E>privatefinalintprivatefinalintintintoffset,intfromIndex,inttoIndex)this.parent=this.parentOffset=this.offset=offset+this.size=toIndex-this.modCount= publicEset(intindex,Eelement)returnl.set(index+offset, public tor<E> tor(finalintindex) private odification()if(ArrayList.this.modCount!=thrownew 58第一,ArrayList了一個(gè)叫作modCount的字段,表示集合結(jié)構(gòu)性修改的次數(shù)。所謂結(jié)構(gòu)性修改,指的是影響List大小的修改,所以add操作必然會(huì)改變modCount的值。2124subListListSubList,并不是普通的ArrayList,在初始化的時(shí)候傳入了this。第三,分析第26到39行代碼可以發(fā)現(xiàn),這個(gè)SubList中的parent字段就是原始的List。SubList初始化的時(shí)候,并沒有把原始List中的元素到獨(dú)立的變量中保存。我們可以認(rèn)SubListListList響,而且SubList強(qiáng)了原始的List,所以大量保存這樣的SubList會(huì)導(dǎo)致OOM第四,分析第4755SubListArrayListmodCountSubListmodCountSubList原始List新增了一個(gè)元素修改了其modCount,所以判等失敗拋出ConcurrentModificationException異常。既然SubListListsubListSubListnewArrayList,在構(gòu)造方法傳入SubList,來構(gòu)建一個(gè)獨(dú)立的ArrayList;Java8StreamskiplimitAPI流中元素的個(gè)數(shù),同樣可以達(dá)到SubList切片的目的。代代12345List<Integer>subList=newArrayList<>(list.subList(1,List<Integer>subList=代代1[2,3,2[1,2,3,4,5,6,7,8,9,344SubListListListList 首先,定義一個(gè)只有一個(gè)int類型訂單號(hào)字段的Order代代staticclassOrderprivateint6elementCountloopCountlistSearchelementCountArrayList,loopCountArrayList,代代privatestaticObjectlistSearch(intelementCount,intloopCount)List<Order>list=IntStream.rangeClosed(1,elementCount).mapToObj(i->IntStream.rangeClosed(1,loopCount).forEach(i->intsearch=Orderresult=list.stream().filter(order->order.getOrderId()==Assert.assertTrue(result!=null&&result.getOrderId()== return9隨后,定義另一個(gè)mapSearch方法,從一個(gè)具有elementCount個(gè)元素的Map中循環(huán)loopCountMapKeyValue代代privatestaticObjectmapSearch(intelementCount,intloopCount)Map<Integer,Order>map=IntStream.rangeClosed(1,IntStream.rangeClosed(1,loopCount).forEach(i->intsearch=Orderresult=Assert.assertTrue(result!=null&&result.getOrderId()== return9我們知道,搜索ArrayList的時(shí)間復(fù)雜度是O(n),而HashMap的get操作的時(shí)間復(fù)雜度是O(1)。所以,要對(duì)大List進(jìn)行單值搜索的話,可以考慮使用HashMap,其中Key是要搜索的值,Value是原始對(duì)象,會(huì)比使用ArrayList有非常明顯的性能優(yōu)勢(shì)。100ArrayListHashMap,listSearchmapSearch1000代代123456789intelementCountintloopCount=;StopWatchstopWatch=newStopWatch();Objectlist=listSearch(elementCount,loopCount);Objectmap=mapSearch(elementCount,loopCount);1000,listSearch3.3mapSearch108代代122345StopWatch'':runningtime%Task 78097%即使我們要搜索的不是單值而是條件區(qū)間,也可以嘗試使用HashMap來進(jìn)行“搜索性能優(yōu)化”。如果你的條件區(qū)間是固定的話,可以提前把HashMap按照條件區(qū)間進(jìn)行分組,Key就是不同的區(qū)間。ArrayListHashMap如果要對(duì)大ArrayList進(jìn)行去重操作,也不建議使用contains方法,而是可以考慮使用HashSet進(jìn)行去重。說到這里,還有一個(gè)問題,使用HashMap是否會(huì)犧牲空間呢?為此,我們使用ObjectSizeCalculator工具打印ArrayList和HashMap的內(nèi)存占用,可以看到ArrayList占用內(nèi)存21M,而HashMap占用的內(nèi)存達(dá)到了72M,是List多。進(jìn)一步使用MAT工具分析堆可以再次證明,ArrayList在內(nèi)存占用上性價(jià)比很高,77%是實(shí)際的數(shù)據(jù)(如第1個(gè)圖所示, ),而HashMap的“含金量”只有22%(如第2個(gè)圖所示, 所以,在應(yīng)用內(nèi)存吃緊的情況下,我們需要考慮是否值得使用的內(nèi)存消耗來?yè)Q取更高的性能。這里我們看到的是平衡的藝術(shù),空間換時(shí)間,還是時(shí)間換空間,只考慮任何一個(gè)方面都是不對(duì)的。第二個(gè)誤區(qū)是,過于教科書的大O時(shí)間復(fù)雜度數(shù)據(jù)結(jié)構(gòu)中要實(shí)現(xiàn)一個(gè)列表,有基于連續(xù)的數(shù)組和基于指針串聯(lián)的鏈表兩種方式。在Java中,有代表性的實(shí)現(xiàn)是ArrayList和LinkedList,前者背后的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,后者 這里,你可以看到數(shù)組和鏈表大O時(shí)間復(fù)雜度的顯著差異:對(duì)于數(shù)組,隨機(jī)元素的時(shí)間復(fù)雜度是O(1),元素插入操作是O(n);對(duì)于鏈表,隨機(jī)元素的時(shí)間復(fù)雜度是O(n),元素插入操作是O(1)。ArrayList,循環(huán)loopCount次,進(jìn)行隨機(jī)和增加元素到隨機(jī)位置的操作代代123456789privatestaticvoidlinkedListGet(intelementCount,intloopCount){List<Integer>list=IntStream.rangeClosed(1,elementCount).boxed().collecIntStream.rangeClosed(1,loopCount).forEach(i->}privatestaticvoidarrayListGet(intelementCount,intloopCount){List<Integer>list=IntStream.rangeClosed(1,elementCount).boxed().collecIntStream.rangeClosed(1,loopCount).forEach(i->}privatestaticvoidlinkedListAdd(intelementCount,intloopCount){List<Integer>list=IntStream.rangeClosed(1,elementCount).boxed().collecIntStream.rangeClosed(1,loopCount).forEach(i->}privatestaticvoidarrayListAdd(intelementCount,intloopCount){List<Integer>list=IntStream.rangeClosed(1,elementCount).boxed().collecIntStream.rangeClosed(1,loopCount).forEach(i->}測(cè)試代碼如下,10萬(wàn)個(gè)元素,循環(huán)10代代123456789intelementCount=100000;intloopCount=100000;StopWatchstopWatch=newStopWatch();linkedListGet(elementCount,loopCount);arrayListGet(elementCount,loopCount);StopWatchstopWatch2=newStopWatch();linkedListAdd(elementCount,loopCount);arrayListAdd(elementCount,loopCount);代39運(yùn)行結(jié)果可能會(huì)讓你眼鏡。在隨機(jī)方面,我們看到了ArrayList的絕對(duì)優(yōu)勢(shì),耗時(shí)只有11毫秒,而LinkedList耗時(shí)6.6但,隨機(jī)插入操作居然也是LinkedList落敗,耗時(shí)9.3秒,ArrayList只要1.5秒:代3912%Task45678StopWatch'':runningtime=1072937883210%Task代123代123456789publicvoidadd(intindex,Eelement)if(index==size)linkBefore(element,}Node<E>node(intindex)assert(index<(size>>{Node<E>x=for(inti=0;i<index;x=return}elseNode<E>x=for(inti=size-1;i>index;i--x=return24}所以,對(duì)于插入操作,LinkedList的時(shí)間復(fù)雜度其實(shí)也是O(n)。繼續(xù)做實(shí)驗(yàn)的話你會(huì)發(fā)現(xiàn),在各種常用場(chǎng)景下,LinkedList幾乎都不能在性能上勝出ArrayList。諷刺的是,LinkedList的作者約書亞·布洛克(JoshBloch),在其上回復(fù)別人時(shí)說,雖然LinkedList是我寫的但我從來不用,有誰(shuí)會(huì)真的用嗎?這告訴我們,任何東西理論上和實(shí)際上是有差距的,教科書的理論,最好在下定論之前實(shí)際測(cè)試一下。拋開算法層面不談,由于CPU緩存、內(nèi)存連續(xù)性等問題,鏈表這種數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)方式對(duì)性能并不友好,即使在它最擅長(zhǎng)的場(chǎng)景都不一定可以發(fā)揮。今天,我了若干和List列表相關(guān)的錯(cuò)誤案例,基本都是由“想當(dāng)然”導(dǎo)致的第一,想當(dāng)然認(rèn)為,Arrays.asListList.subListListArrays.asList得到的是Arrays的內(nèi)部類ArrayList,List.subList得到的是ArrayList的內(nèi)部類SubList,不能把這兩個(gè)內(nèi)部類轉(zhuǎn)換為ArrayList使用。Arrays.asList直接使用了原始數(shù)組,可以認(rèn)為是共享“”,而且不支持增刪元素;List.subList直接了原始的List,也可以認(rèn)為是共享“”,而且對(duì)原始List直接進(jìn)行結(jié)構(gòu)性修改會(huì)導(dǎo)致SubList出現(xiàn)異常。對(duì)Arrays.asList和List.subList容易忽略的是,新的List持有了原始數(shù)據(jù)的,可能會(huì)導(dǎo)致原始數(shù)據(jù)也無法GC的
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 品質(zhì)管控培訓(xùn)質(zhì)量管理與質(zhì)量控制課件
- 消化內(nèi)科內(nèi)窺鏡治療創(chuàng)新
- 綠色創(chuàng)新:促進(jìn)綠色出行習(xí)慣養(yǎng)成
- 公文寫作講話稿范文
- 市婦聯(lián)社區(qū)未成年人思想道德建設(shè)情況總結(jié) 總結(jié)
- 世界急救日活動(dòng)總結(jié)
- 骨折護(hù)理-加速骨折愈合與功能恢復(fù)
- 市政施工合同范例簡(jiǎn)短
- 律師委托代理合同范例
- 勞動(dòng)合同范例 景區(qū)
- 全科門診教學(xué)知情同意書
- 人文地理與城鄉(xiāng)規(guī)劃專業(yè)職業(yè)生涯規(guī)劃書
- GB 6514-2023涂裝作業(yè)安全規(guī)程涂漆工藝安全及其通風(fēng)
- 工程倫理 課件第8、9章 工程、健康與可持續(xù)發(fā)展;全球化視野下的工程倫理
- 汽車防盜系統(tǒng)維修從入門到精通
- 云服務(wù)門禁管理系統(tǒng)
- 2024醫(yī)藥行業(yè)政策分析
- 雨污分流監(jiān)理實(shí)施細(xì)則
- DD 2022-1.2 巖心數(shù)字化技術(shù)規(guī)程 第2部分:表面圖像數(shù)字化
- 全國(guó)優(yōu)質(zhì)課一等獎(jiǎng)初中物理九年級(jí)《科學(xué)探究:歐姆定律》課件
- 中醫(yī)外科乳房疾病診療規(guī)范診療指南2023版
評(píng)論
0/150
提交評(píng)論