摘要:在反射方面來說,從運行時返回一個的實例時不需要經(jīng)過強制轉換然后則需要經(jīng)過轉換才能得到。如果對數(shù)據(jù)的數(shù)量大小已知,操作也非常簡單,也不需要中的大部分方法,也是可以直接使用數(shù)組的。
我在想每個人在面試的時候都會被問到集合相關的問題,有好大一部分人在回答的時候并沒有那么多的邏輯性,通常都是想到哪里說到哪里,這篇文章大概的捋一捋關于集合的相關問題。
在每種編程語言中,都會有循環(huán)、數(shù)組、流程控制語句,數(shù)組是一種線性表數(shù)據(jù)結構,內(nèi)存空間是連續(xù)的,保存的數(shù)據(jù)類型也是一致的。
正是因為這兩點,數(shù)組的隨機訪問才會非常的高效,這同時也是一把雙刃劍,使得數(shù)組的其他操作效率變得很低,比如說,增加,刪除,為了保持數(shù)組里面數(shù)據(jù)的連續(xù)性,就會做大量的消耗性能的數(shù)據(jù)遷移操作。
針對數(shù)組這種類型,java中有容器類,比如ArrayList,ArrayList是對數(shù)組的包裝,在底層就是數(shù)組實現(xiàn)的,因為數(shù)組在定義的時候必須是指定的長度,定義之后就無法再增加長度了,就是說不可能在原來的數(shù)組上接上一段,所以ArrayList就解決了這個問題,當超過數(shù)組容量的時候,ArrayList會進行擴容,擴容之后的容量是之前的1.5倍,然后再把之前數(shù)組中的數(shù)據(jù)復制過來。
接下來,我們看下ArrayList的源碼:
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
源碼中數(shù)組最大的容量是Integer.MAX_VALUE -8,為什么要減去8 呢,這個是上面的定義上面的注釋:
/** * The maximum size of array to allocate. * Some VMs reserve some header words in an array. * Attempts to allocate larger arrays may result in * OutOfMemoryError: Requested array size exceeds VM limit */
首先是一些VM在數(shù)組中保存的頭信息;嘗試著去分配更大的數(shù)組可能會導致OutOfMemoryError,請求的數(shù)組大小超過了VM的限制。
在看這句代碼的時候,腦中有沒有出現(xiàn)兩個大大的問號??
首先,有沒有想到為什么這個數(shù)組屬性需要用 transient修飾?
(想知道這個關鍵字是干什么的,可以看下我之前的一篇文章:面試問你java中的序列化怎么答?)
transient Object[] elementData; // non-private to simplify nested class access
大家可以隨便的想一下,如果是面試的時候,你會怎么回答?
由于 ArrayList 是基于動態(tài)數(shù)組實現(xiàn)的,所以并不是所有的空間都被使用。因此使用了 transient 修飾,可以防止被自動序列化。
因此 ArrayList 自定義了序列化與反序列化,具體可以看 writeObject 和 readObject 兩個方法。
需要注意的一點是,當對象中自定義了 writeObject 和 readObject 方法時,JVM 會調(diào)用這兩個自定義方法來實現(xiàn)序列化與反序列化。
第二個問題:這個屬性的類型為什么是Object而不是泛型?
這里和大家說下:
java中泛型運用的目的就是對象的重用,就是同一個方法,可以支持多種對象類型,Object和泛型在編寫的時候其實沒有太大的區(qū)別,只是JVM中沒有T這個概念,T只是存在編寫的時候,進入虛擬機運行的時候,虛擬機會對這種泛型標志進行擦除,也就是替換T到指定的類型,如果沒有指定類型,就會用Object替換,同時Object可以new Object(),就是說可以實例化,而T則不能實例化。在反射方面來說,從運行時,返回一個T的實例時,不需要經(jīng)過強制轉換,然后Object則需要經(jīng)過轉換才能得到。
當我們試圖向ArrayList中添加一個元素的時候,java會自動檢查,以確保集合中確實還有容量來添加新元素,如果沒有,就會自動擴容,下面是核心代碼,我已經(jīng)在代碼里加了注釋,幫助大家能夠更好的理解:
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
上面這段代碼中有個 變量 modCount++,看到這里的時候確實有些疑惑,我找了下,這個變量是在AbstractList中定義的protected修飾的全局變量,這個變量是記錄了結構性改變的次數(shù),結構性改變就是說修改列表大小的操作。
ArrayList是一個線程不安全的類,這個變量就是用來保證在多線程環(huán)境下使用迭代器的時候,同時又對集合進行了修改,同一時刻只能有一個線程修改集合,如果多于一個,就會拋出ConcurrentModficationException。
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //核心的擴容代碼:擴容之后的容量, int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) //擴容之后的容量與本次操作需要的容量對比,取更大的 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) //在與數(shù)組的最大容量對比,如果比最大的容量大,進入下一個方法 newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: //接下來,是把原數(shù)組d 數(shù)據(jù)復制到新的數(shù)組里 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //當 Integer-8 依然無法滿足需求,就會取Integer的最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
接下來我們看下,向指定位置添加元素是什么樣的:
public void add(int index, E element) { rangeCheckForAdd(index); //擴容校驗 ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
可以看到,當一個集合足夠大的時候,add操作向數(shù)組的尾部添加元素效率還是非常高的,但是當向指定的位置添加元素的時候,也是需要大量的移動復制操作:System.arraycopy()。
到這里,ArrayList最大的優(yōu)勢是什么呢?我們在平時的開發(fā)中涉及到容器的時候為什么會選擇List家族的成員,而不直接選擇數(shù)組呢?大家回想一下自己之前敲代碼的經(jīng)歷,答案也就出來了:
ArrayList封裝了大部分數(shù)組的操作方法,比如插入、刪除、搬移數(shù)據(jù)等等,都在集合內(nèi)部幫你做好了,還有就是支持動態(tài)擴容,這點是數(shù)組不能比擬的。
這里需要注意一點,當我們在開始定義集合的時候,如果知道我們需要多大的集合,就應該在一開始就指定集合的大小,因為在集合的內(nèi)部來進行數(shù)據(jù)的搬移,復制也是非常耗時的。
那么數(shù)組在什么時候會用到呢?
1、java ArrayList無法存儲基本類型,int,long,如果保存的話,就需要封裝為Integer、Long,而自動的拆裝箱,也有性能的消耗,所以總結下這點就是說如果要保存基本類型,同時還特別關注性能,就可以使用數(shù)組。
2、如果對數(shù)據(jù)的數(shù)量大小已知,操作也非常簡單,也不需要ArrayList中的大部分方法,也是可以直接使用數(shù)組的。
如果對本文有任何異議,可以加我好友(有沒有問題都歡迎大家加我好友),也可以在下面留言區(qū)留言,我會及時修改。希望這篇文章能幫助大家在面試路上乘風破浪。
這樣的分享我會一直持續(xù),你的關注、轉發(fā)和好看是對我最大的支持,感謝。關注我,我們一起成長。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74151.html
摘要:背景在開發(fā)好頁面后,如何讓頁面更快更好的運行,是區(qū)分一個程序猿技術水平和視野的一個重要指標。在對這些環(huán)節(jié)進行優(yōu)化之前,我們需要知道如何監(jiān)控這些環(huán)節(jié)花費了多少時間。為了優(yōu)化鏈接的環(huán)節(jié),前端這里還需要對資源使用,雪碧圖,代碼合并等手段。 背景 在開發(fā)好頁面后,如何讓頁面更快更好的運行,是區(qū)分一個程序猿技術水平和視野的一個重要指標。所以面試時,面試官總會問你一個問題,如何進行性能優(yōu)化呢? 如...
摘要:正因為如此,現(xiàn)在很多簡歷上的項目經(jīng)歷的質量都是參差不齊,同時有的項目經(jīng)歷又非常相似,面試官一眼就能知道你的項目到底是真是假。雖然以上三點原則不能包治百病,但是對很多同學來說應該是蠻有益處的。閱讀本文大概需要 5 分鐘。作者:黃小斜showImg(https://user-gold-cdn.xitu.io/2019/3/30/169cdb4bd2cac24c);?作為一個程序員,想必大家曾經(jīng)都...
摘要:正確做法是給加索引,還有聯(lián)合索引,并不能避免全表掃描。 前言:有收獲的話請加顆小星星,沒有收獲的話可以 反對 沒有幫助 舉報三連 有心的同學應該會看到我這個noteBook下面的其它知識,希望對你們有些許幫助。 本文地址 時間點:2017-11 一個16年畢業(yè)生所經(jīng)歷的php面試 一、什么是面試 二、面試準備 1. 問:什么時候開始準備? 2. 問:怎么準備? 三、面試...
閱讀 3208·2021-09-30 09:48
閱讀 3485·2021-09-22 16:00
閱讀 1062·2019-08-30 13:08
閱讀 3098·2019-08-30 10:53
閱讀 2410·2019-08-29 18:33
閱讀 1583·2019-08-29 12:47
閱讀 896·2019-08-29 12:16
閱讀 1929·2019-08-26 12:02