基于ArrayList初始化長(zhǎng)度的作用及影響
平時(shí)寫代碼都直接寫
List<String> list = new ArrayList<>();
由于公司做政.府項(xiàng)目,對(duì)并發(fā)和響應(yīng)沒(méi)有太苛刻的要求,平時(shí)就沒(méi)有考慮到這一塊。
今天看同事代碼在new ArrayList<>()的時(shí)候帶入初始容量,于是好奇百度一下,講結(jié)果記錄下來(lái)。
一、有無(wú)初始容量的區(qū)別
?? ?/** ? ? ?* 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 ? ? ?*/ ? ? private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; ? ?? ?? ?/** ? ? ?* Default initial capacity. ? ? ?*/ ? ? private static final int DEFAULT_CAPACITY = 10; ?? ?/** ? ? ?* The array buffer into which the elements of the ArrayList are stored. ? ? ?* The capacity of the ArrayList is the length of this array buffer. Any ? ? ?* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA ? ? ?* will be expanded to DEFAULT_CAPACITY when the first element is added. ? ? ?*/ ? ? transient Object[] elementData; // non-private to simplify nested class access ?? ?/** ? ? ?* Shared empty array instance used for empty instances. ? ? ?*/ ? ? private static final Object[] EMPTY_ELEMENTDATA = {};?? ? ?? ?/** ? ? ?* Constructs an empty list with the specified initial capacity. ? ? ?* ? ? ?* @param ?initialCapacity ?the initial capacity of the list ? ? ?* @throws IllegalArgumentException if the specified initial capacity ? ? ?* ? ? ? ? is negative ? ? ?*/ ? ? public ArrayList(int initialCapacity) { ? ? ? ? if (initialCapacity > 0) { ? ? ? ? ? ? this.elementData = new Object[initialCapacity]; ? ? ? ? } else if (initialCapacity == 0) { ? ? ? ? ? ? this.elementData = EMPTY_ELEMENTDATA; ? ? ? ? } else { ? ? ? ? ? ? throw new IllegalArgumentException("Illegal Capacity: "+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?initialCapacity); ? ? ? ? } ? ? } ?? ?/** ? ? ?* Increases the capacity to ensure that it can hold at least the ? ? ?* number of elements specified by the minimum capacity argument. ? ? ?* ? ? ?* @param minCapacity the desired minimum capacity ? ? ?*/ ? ? 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) ? ? ? ? ? ? newCapacity = hugeCapacity(minCapacity); ? ? ? ? // minCapacity is usually close to size, so this is a win: ? ? ? ? elementData = Arrays.copyOf(elementData, newCapacity); ? ? }
以上是JDK1.8的ArrayList源碼,可以看出,
- 沒(méi)有初始容量的話,在做數(shù)據(jù)操作的時(shí)候ArrayList會(huì)自己創(chuàng)建容量,JDK1.8默認(rèn)為10
- 每次擴(kuò)容后容量為oldCapacity + (oldCapacity >> 1)
- 容量最大值Integer.MAX_VALUE - 8
由此可以想到,如果存在上千上萬(wàn)數(shù)據(jù)量的操作,不初始容量和初始化了合適的容量,處理時(shí)間肯定不同,因?yàn)槌跏蓟蛿U(kuò)容是需要時(shí)間的。
測(cè)試代碼如下:
public static void main(String[] args) { ? ? final int count = 200 * 10000; ? ? List<Integer> list = new ArrayList<>(); ? ? long begin = System.currentTimeMillis(); ? ? for(int i = 0; i < count ; i++) { ? ? ? ? list.add(i); ? ? } ? ? System.out.println("沒(méi)有設(shè)置ArrayList初始容量: " + (System.currentTimeMillis() - begin) + " ms"); ? ? List<Integer> list2 = new ArrayList<>(10); ? ? long begin2 = System.currentTimeMillis(); ? ? for(int i = 0; i < count ; i++) { ? ? ? ? list2.add(i); ? ? } ? ? System.out.println("設(shè)置了ArrayList初始容量: " + (System.currentTimeMillis() - begin2) + " ms"); }
輸出:
沒(méi)有設(shè)置ArrayList初始容量: 96 ms
設(shè)置了ArrayList初始容量: 26 ms
分析:
在list.add()方法執(zhí)行時(shí),先調(diào)用ArrayList的:
/** ?* Appends the specified element to the end of this list. ?* ?* @param e element to be appended to this list ?* @return <tt>true</tt> (as specified by {@link Collection#add}) ?*/ public boolean add(E e) { ? ? ensureCapacityInternal(size + 1); ?// Increments modCount!! ? ? elementData[size++] = e; ? ? return true; }
進(jìn)入方法:
private void ensureCapacityInternal(int minCapacity) { ? ? ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
再往下:
private static int calculateCapacity(Object[] elementData, int minCapacity) { ? ? if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 第一次add的時(shí)候,都會(huì)走這一步 ? ? ? ? return Math.max(DEFAULT_CAPACITY, minCapacity);//初始化容量小于默認(rèn)值10都會(huì)取10,反之取自定義的容量 ? ? } ? ? return minCapacity; }
擴(kuò)容方法:
private void ensureExplicitCapacity(int minCapacity) { ? ? modCount++; ? ? // overflow-conscious code ? ? if (minCapacity - elementData.length > 0) ? ? ? ? grow(minCapacity); }
grow():
/** ?* Increases the capacity to ensure that it can hold at least the ?* number of elements specified by the minimum capacity argument. ?* ?* @param minCapacity the desired minimum capacity ?*/ ?private void grow(int minCapacity) {//minCapacity是當(dāng)前容量,比如,默認(rèn)容量下,add一次后就是10+1 ? ? // overflow-conscious code ? ? int oldCapacity = elementData.length; ? ? int newCapacity = oldCapacity + (oldCapacity >> 1); ? ? if (newCapacity - minCapacity < 0) ? ? ? ? newCapacity = minCapacity; ? ? if (newCapacity - MAX_ARRAY_SIZE > 0) ? ? ? ? newCapacity = hugeCapacity(minCapacity); ? ? // minCapacity is usually close to size, so this is a win: ? ? elementData = Arrays.copyOf(elementData, newCapacity); }
總結(jié):
- 建議初始化容量,減少系統(tǒng)初始化容量的耗時(shí);
- 初始化容量不是越大越好,跟系統(tǒng)配置相關(guān),因?yàn)橐_辟內(nèi)存。如果能確定add的總數(shù),以總數(shù)作為初始容量效率最高,但這種場(chǎng)景太少了。最佳的設(shè)置要兼顧內(nèi)存空間和擴(kuò)容次數(shù),我也沒(méi)有找到最優(yōu)解,歡迎大佬補(bǔ)充。
- 盡管不知道初始化多少最快,但是初始化比未初始化快,并且有限的數(shù)據(jù)量下,設(shè)置不同initialCapacity的差距不大。最終,我建議大家初始化容量,并且就寫10(<=10都一樣,看自己喜好)。
上例不同大小初始容量的耗時(shí):
initialCapacity | time |
---|---|
未初始化 | 96 |
<=10 | 26 |
100 | 26 |
1000 | 23 |
10000 | 648 |
100000 | 24 |
1000000 | 18 |
10000000 | 609 |
二、initialCapacity != list.size()
public static void main(String[] args) { ? ? List<Integer> list = new ArrayList<>(10); ? ? list.set(0, 666); }
console:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.set(ArrayList.java:448)
at top.chengsw.demo.test.ListTest.main(ListTest.java:25)
此時(shí),list.size() = 0。
也就是說(shuō),該構(gòu)造方法并不是將ArrayList()初始化為指定長(zhǎng)度,而是指定了其內(nèi)部的Object數(shù)組的長(zhǎng)度,也就是其容量。
當(dāng)我們調(diào)用size()時(shí),返回的是其實(shí)際長(zhǎng)度,而非容量大小。
對(duì)超出ArrayList長(zhǎng)度的部分進(jìn)行訪問(wèn)或賦值操作時(shí)也會(huì)造成訪問(wèn)越界,盡管它的容量大小足夠。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
JavaWeb實(shí)現(xiàn)郵件發(fā)送功能
這篇文章主要為大家詳細(xì)介紹了JavaWeb實(shí)現(xiàn)郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12利用Java寫一個(gè)學(xué)生管理系統(tǒng)
今天這篇文章就給給大家分享利用Java寫一個(gè)學(xué)生管理系統(tǒng)吧,先寫一個(gè)簡(jiǎn)單的用List來(lái)實(shí)現(xiàn)學(xué)生管理系統(tǒng):2021-09-09Spring之關(guān)于PropertyDescriptor的擴(kuò)展剖析
這篇文章主要介紹了Spring之關(guān)于PropertyDescriptor的擴(kuò)展剖析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java使用System.currentTimeMillis()方法計(jì)算程序運(yùn)行時(shí)間的示例代碼
System.currentTimeMillis() 方法的返回類型為 long ,表示毫秒為單位的當(dāng)前時(shí)間,文中通過(guò)示例代碼介紹了計(jì)算 String 類型與 StringBuilder 類型拼接字符串的耗時(shí)情況,對(duì)Java計(jì)算程序運(yùn)行時(shí)間相關(guān)知識(shí)感興趣的朋友一起看看吧2022-03-03Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之單向鏈表
單向鏈表特點(diǎn)是鏈表的鏈接方向是單向的,訪問(wèn)要通過(guò)順序讀取從頭部開始。鏈表是使用指針構(gòu)造的列表,是由一個(gè)個(gè)結(jié)點(diǎn)組裝起來(lái)的,又稱為結(jié)點(diǎn)列表。其中每個(gè)結(jié)點(diǎn)都有指針成員變量指向列表中的下一個(gè)結(jié)點(diǎn),head指針指向第一個(gè)結(jié)點(diǎn)稱為表頭,而終止于最后一個(gè)指向nuLL的指針2022-02-02解決StringBuffer和StringBuilder的擴(kuò)容問(wèn)題
這篇文章主要介紹了解決StringBuffer和StringBuilder的擴(kuò)容問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot接入輕量級(jí)分布式日志框架(GrayLog)的操作方法
這篇文章主要介紹了SpringBoot接入輕量級(jí)分布式日志框架(GrayLog)的方法,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03JAVA JNI函數(shù)的注冊(cè)過(guò)程詳細(xì)介紹
這篇文章主要介紹了JAVA JNI函數(shù)的注冊(cè)過(guò)程詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-11-11