摘要:關(guān)于的具體實(shí)現(xiàn),一些基本的都也知道,譬如數(shù)組實(shí)現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如初始化的長(zhǎng)度,擴(kuò)容等。
前言
在之前的文章中我們提到過(guò)ArrayList,ArrayList可以說(shuō)是每一個(gè)學(xué)java的人使用最多最熟練的集合了,但是知其然不知其所以然。關(guān)于ArrayList的具體實(shí)現(xiàn),一些基本的都也知道,譬如數(shù)組實(shí)現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如:初始化的長(zhǎng)度,擴(kuò)容等。
本篇主要通過(guò)一些對(duì)源碼的分析,講解幾個(gè)ArrayList常見(jiàn)的方法,以及和Vector的區(qū)別。
ArrayList 定義public class ArrayListextends AbstractList implements List , RandomAccess, Cloneable, java.io.Serializable
ArrayList實(shí)際上是一個(gè)動(dòng)態(tài)數(shù)組,容量可以動(dòng)態(tài)的增長(zhǎng),其繼承了AbstractList,實(shí)現(xiàn)了List, RandomAccess, Cloneable, java.io.Serializable這些接口。
RandomAccess接口,標(biāo)記接口,表明List提供了隨機(jī)訪問(wèn)功能,也就是通過(guò)下標(biāo)獲取元素對(duì)象的功能。
1.RandomAccess接口,標(biāo)記接口,表明List提供了隨機(jī)訪問(wèn)功能,也就是通過(guò)下標(biāo)獲取元素對(duì)象的功能。之所以是標(biāo)記接口,是該類(lèi)本來(lái)就具有某項(xiàng)能力,使用接口對(duì)其進(jìn)行標(biāo)簽化,便于其他的類(lèi)對(duì)其進(jìn)行識(shí)別(instanceof)。
2.ArrayList數(shù)組實(shí)現(xiàn),本身就有通過(guò)下標(biāo)隨機(jī)訪問(wèn)任意元素的功能。那么需要細(xì)節(jié)上注意的就是隨機(jī)下標(biāo)訪問(wèn)和順序下標(biāo)訪問(wèn)(LinkedList)的不同了。也就是為什么LinkedList最好不要使用循環(huán)遍歷,而是用迭代器遍歷的原因。
3.實(shí)現(xiàn)RandomAccess同時(shí)意味著一些算法可以通過(guò)類(lèi)型判斷進(jìn)行一些針對(duì)性?xún)?yōu)化,例子有Collections的shuffle方法,源代碼就不粘貼了,簡(jiǎn)單說(shuō)就是,如果實(shí)現(xiàn)RandomAccess接口就下標(biāo)遍歷,反之迭代器遍歷
實(shí)現(xiàn)了Cloneable, java.io.Serializable意味著可以被克隆和序列化。
初始化在使用中我們經(jīng)常需要new出來(lái)各種泛型的ArrayList,那么在初始化過(guò)程是怎樣的呢?
如下一行代碼,創(chuàng)建一個(gè)ArrayList對(duì)象
Listlist = new ArrayList<>();
我們來(lái)看源碼,是如何初始化的,找到構(gòu)造方法
//無(wú)參構(gòu)造方法 public ArrayList() { super(); this.elementData = EMPTY_ELEMENTDATA; }
看到這些代碼的時(shí)候,我也是不解的,elementData和EMPTY_ELEMENTDATA是什么啊?但是很明顯EMPTY_ELEMENTDATA是一個(gè)常量,追本溯源我們?nèi)タ匆幌鲁蓡T屬性。
//如果是無(wú)參構(gòu)造方法創(chuàng)建對(duì)象的話,ArrayList的初始化長(zhǎng)度為10,這是一個(gè)靜態(tài)常量 private static final int DEFAULT_CAPACITY = 10; //在這里可以看到我們不解的EMPTY_ELEMENTDATA實(shí)際上是一個(gè)空的對(duì)象數(shù)組 private static final Object[] EMPTY_ELEMENTDATA = {}; //保存ArrayList數(shù)據(jù)的對(duì)象數(shù)組緩沖區(qū) 空的ArrayList的elementData = EMPTY_ELEMENTDATA 這就是為什么說(shuō)ArrayList底層是數(shù)組實(shí)現(xiàn)的了。elementData的初始容量為10,大小會(huì)根據(jù)ArrayList容量的增長(zhǎng)而動(dòng)態(tài)的增長(zhǎng)。 private transient Object[] elementData; //集合的長(zhǎng)度 private int size;
執(zhí)行完構(gòu)造方法,如下圖
可以說(shuō)ArrayList的作者真的是很貼心,連緩存都處理好了,多次new出來(lái)的新對(duì)象,都指向同一個(gè)引用。
添加方法add/** * Appends the specified element to the end of this list. */ //增加元素到集合的最后 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! //因?yàn)?+運(yùn)算符的特點(diǎn) 先使用后運(yùn)算 這里實(shí)際上是 //elementData[size] = e //size+1 elementData[size++] = e; return true; }
先不管ensureCapacityInternal的話,這個(gè)方法就是將一個(gè)元素增加到數(shù)組的size++位置上。
再說(shuō)回ensureCapacityInternal,它是用來(lái)擴(kuò)容的,準(zhǔn)確說(shuō)是用來(lái)進(jìn)行擴(kuò)容檢查的。下面我們來(lái)看一下整個(gè)擴(kuò)容的過(guò)程
//初始長(zhǎng)度是10,size默認(rèn)值0,假定添加的是第一個(gè)元素,那么minCapacity=1 這是最小容量 如果小于這個(gè)容量就會(huì)報(bào)錯(cuò) //如果底層數(shù)組就是默認(rèn)數(shù)組,那么選一個(gè)大的值,就是10 private void ensureCapacityInternal(int minCapacity) { if (elementData == EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //調(diào)用另一個(gè)方法ensureExplicitCapacity ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { //記錄修改的次數(shù) modCount++; // overflow-conscious code //如果傳過(guò)來(lái)的值大于初始長(zhǎng)度 執(zhí)行g(shù)row方法(參數(shù)為傳過(guò)來(lái)的值)擴(kuò)容 if (minCapacity - elementData.length > 0) grow(minCapacity); } //真正的擴(kuò)容 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //新的容量是在原有的容量基礎(chǔ)上+50% 右移一位就是二分之一 int newCapacity = oldCapacity + (oldCapacity >> 1); //如果新容量小于最小容量,按照最小容量進(jìn)行擴(kuò)容 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: //這里是重點(diǎn) 調(diào)用工具類(lèi)Arrays的copyOf擴(kuò)容 elementData = Arrays.copyOf(elementData, newCapacity); } //Arrays public staticT[] copyOf(U[] original, int newLength, Class extends T[]> newType) { T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] : (T[]) Array.newInstance(newType.getComponentType(), newLength); System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
添加到指定的位置
public void add(int index, E element) { //判斷索引是否越界,如果越界就會(huì)拋出下標(biāo)越界異常 rangeCheckForAdd(index); //擴(kuò)容檢查 ensureCapacityInternal(size + 1); // Increments modCount!! //將指定下標(biāo)空出 具體作法就是index及其后的所有元素后移一位 System.arraycopy(elementData, index, elementData, index + 1,size - index); //將要添加元素賦值到空出來(lái)的指定下標(biāo)處 elementData[index] = element; //長(zhǎng)度加1 size++; } //判斷是否出現(xiàn)下標(biāo)是否越界 private void rangeCheckForAdd(int index) { //如果下標(biāo)超過(guò)了集合的尺寸 或者 小于0就是越界 if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
ArrayList支持兩種刪除元素的方式
remove(int index) 按照下標(biāo)刪除 常用
remove(Object o) 按照元素刪除 會(huì)刪除和參數(shù)匹配的第一個(gè)元素
下面我們看一下ArrayList的實(shí)現(xiàn)
/** 移除list中指定位置的元素 * Removes the element at the specified position in this list. 所有后續(xù)元素左移 下標(biāo)減1 * Shifts any subsequent elements to the left (subtracts one from their * indices). *參數(shù)是要移除元素的下標(biāo) * @param index the index of the element to be removed 返回值是被移除的元素 * @return the element that was removed from the list * @throws IndexOutOfBoundsException {@inheritDoc} */ public E remove(int index) { //下標(biāo)越界檢查 rangeCheck(index); //修改次數(shù)統(tǒng)計(jì) modCount++; //獲取這個(gè)下標(biāo)上的值 E oldValue = elementData(index); //計(jì)算出需要移動(dòng)的元素個(gè)數(shù) (因?yàn)樾枰獙⒑罄m(xù)元素左移一位 此處計(jì)算出來(lái)的是后續(xù)元素的位數(shù)) int numMoved = size - index - 1; //如果這個(gè)值大于0 說(shuō)明后續(xù)有元素需要左移 是0說(shuō)明被移除的對(duì)象就是最后一位元素 if (numMoved > 0) //索引index只有的所有元素左移一位 覆蓋掉index位置上的元素 System.arraycopy(elementData, index+1, elementData, index,numMoved); // 將最后一個(gè)元素賦值為null 這樣就可以被gc回收了 elementData[--size] = null; // clear to let GC do its work //返回index位置上的元素 return oldValue; } //移除特定元素 public boolean remove(Object o) { //如果元素是null 遍歷數(shù)組移除第一個(gè)null if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { //遍歷找到第一個(gè)null元素的下標(biāo) 調(diào)用下標(biāo)移除元素的方法 fastRemove(index); return true; } } else { //找到元素對(duì)應(yīng)的下標(biāo) 調(diào)用下標(biāo)移除元素的方法 for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } //按照下標(biāo)移除元素 private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
底層數(shù)組實(shí)現(xiàn),使用默認(rèn)構(gòu)造方法初始化出來(lái)的容量是10
擴(kuò)容的長(zhǎng)度是在原長(zhǎng)度基礎(chǔ)上加二分之一
實(shí)現(xiàn)了RandomAccess接口,底層又是數(shù)組,get讀取元素性能很好
線程不安全,所有的方法均不是同步方法也沒(méi)有加鎖,因此多線程下慎用
順序添加很方便
刪除和插入需要復(fù)制數(shù)組 性能很差(可以使用LinkindList)
transient修飾的屬性意味著不會(huì)被序列化,也就是說(shuō)在序列化ArrayList的時(shí)候,不序列化elementData。
為什么要這么做呢?
elementData不總是滿的,每次都序列化,會(huì)浪費(fèi)時(shí)間和空間
重寫(xiě)了writeObject 保證序列化的時(shí)候雖然不序列化全部 但是有的元素都序列化
所以說(shuō)不是不序列化 而是不全部序列化。
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out array length s.writeInt(elementData.length); // Write out all elements in the proper order. for (int i=0; iArrayList和Vector的區(qū)別 標(biāo)準(zhǔn)答案
ArrayList是線程不安全的,Vector是線程安全的
擴(kuò)容時(shí)候ArrayList擴(kuò)0.5倍,Vector擴(kuò)1倍
那么問(wèn)題來(lái)了
ArrayList有沒(méi)有辦法線程安全?
Collections工具類(lèi)有一個(gè)synchronizedList方法
可以把list變?yōu)榫€程安全的集合,但是意義不大,因?yàn)榭梢允褂肰ector
Vector為什么是線程安全的?
老實(shí)講,拋開(kāi)多線程 它倆區(qū)別沒(méi)多大,但是多線程下就不一樣了,那么Vector是如何實(shí)現(xiàn)線程安全的,我們來(lái)看幾個(gè)關(guān)鍵方法
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; } public synchronized E remove(int index) { modCount++; if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); E oldValue = elementData(index); int numMoved = elementCount - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--elementCount] = null; // Let gc do its work return oldValue; }就代碼實(shí)現(xiàn)上來(lái)說(shuō),和ArrayList并沒(méi)有很多邏輯上的區(qū)別,但是在Vector的關(guān)鍵方法都使用了synchronized修飾。
我不能保證每一個(gè)地方都是對(duì)的,但是可以保證每一句話,每一行代碼都是經(jīng)過(guò)推敲和斟酌的。希望每一篇文章背后都是自己追求純粹技術(shù)人生的態(tài)度。永遠(yuǎn)相信美好的事情即將發(fā)生。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/68232.html
摘要:關(guān)于的具體實(shí)現(xiàn),一些基本的都也知道,譬如數(shù)組實(shí)現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如初始化的長(zhǎng)度,擴(kuò)容等。 前言 在之前的文章中我們提到過(guò)ArrayList,ArrayList可以說(shuō)是每一個(gè)學(xué)java的人使用最多最熟練的集合了,但是知其然不知其所以然。關(guān)于ArrayList的具體實(shí)現(xiàn),一些基本的都也知道,譬如數(shù)組實(shí)現(xiàn),線程不安全等等,但是更加具體的就很少去了解了,例如:...
摘要:前言在上篇文章中我們對(duì)對(duì)了詳細(xì)的分析,今天我們來(lái)說(shuō)一說(shuō)。他們之間有什么區(qū)別呢最大的區(qū)別就是底層數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)不一樣,是數(shù)組實(shí)現(xiàn)的具體看上一篇文章,是鏈表實(shí)現(xiàn)的。至于其他的一些區(qū)別,可以說(shuō)大部分都是由于本質(zhì)不同衍生出來(lái)的不同應(yīng)用。 前言 在上篇文章中我們對(duì)ArrayList對(duì)了詳細(xì)的分析,今天我們來(lái)說(shuō)一說(shuō)LinkedList。他們之間有什么區(qū)別呢?最大的區(qū)別就是底層數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)不一樣,...
摘要:再者,現(xiàn)在互聯(lián)網(wǎng)的面試中上點(diǎn)的都會(huì)涉及一下或者的問(wèn)題個(gè)高級(jí)多線程面試題及回答后端掘金在任何面試當(dāng)中多線程和并發(fā)方面的問(wèn)題都是必不可少的一部分。假如源碼分析之掘金概念是中集合的一種實(shí)現(xiàn)。 攻破 JAVA NIO 技術(shù)壁壘 - 后端 - 掘金現(xiàn)在使用NIO的場(chǎng)景越來(lái)越多,很多網(wǎng)上的技術(shù)框架或多或少的使用NIO技術(shù),譬如Tomcat,Jetty。學(xué)習(xí)和掌握NIO技術(shù)已經(jīng)不是一個(gè)JAVA攻城獅...
摘要:需要注意的是,通過(guò)構(gòu)造函數(shù)定義初始量是動(dòng)態(tài)數(shù)組的實(shí)際大小。帶容量的構(gòu)造函數(shù)新建一個(gè)容量為的數(shù)組默認(rèn)構(gòu)造函數(shù),默認(rèn)為空構(gòu)造一個(gè)包含指定元素的第一個(gè)構(gòu)造方法使用提供的來(lái)初始化數(shù)組的大小。 前言 今天介紹經(jīng)常使用的一個(gè)Java集合類(lèi)——ArrayList(基于JDK1.8.0_121)。ArrayList在工作和日常面試中經(jīng)常被使用或者提到。總的來(lái)說(shuō),工作中使用ArrayList主要是因?yàn)閯?dòng)...
閱讀 3077·2023-04-26 00:53
閱讀 3522·2021-11-19 09:58
閱讀 1693·2021-09-29 09:35
閱讀 3279·2021-09-28 09:46
閱讀 3852·2021-09-22 15:38
閱讀 2692·2019-08-30 15:55
閱讀 3006·2019-08-23 14:10
閱讀 3822·2019-08-22 18:17