Java進(jìn)階--深入理解ArrayList實(shí)現(xiàn)原理

ArrayList簡介

ArrayList就是動(dòng)態(tài)數(shù)組或舞,用MSDN中的說法荆姆,就是Array的復(fù)雜版本,它提供了動(dòng)態(tài)的增加和減少元素嚷那,實(shí)現(xiàn)了Collection和List接口胞枕,可以靈活的設(shè)置數(shù)組的大小。要注意的是ArrayList并不是線程安全的魏宽,因此一般建議在單線程中使用ArrayList腐泻。


ArrayList的繼承關(guān)系

20170519105333354.jpg
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

由上可知ArrayList繼承AbstractList 并且實(shí)現(xiàn)了List和RandomAccess,Cloneable, Serializable接口队询。


ArrayList的方法使用和源碼解析

①構(gòu)造方法

//1-----------------------
public ArrayList() {
        this(10);
        //調(diào)用ArrayList(10) 默認(rèn)初始化一個(gè)大小為10的object數(shù)組派桩。
    }
    
//2-------------------------
public ArrayList(int initialCapacity) {    
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
         //如果用戶初始化大小小于0拋異常,否則新建一個(gè)用戶初始值大小的object數(shù)組蚌斩。                                      
        this.elementData = new Object[initialCapacity];
    } 
    
//3--------------------------
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // 當(dāng)c.toArray返回的不是object類型的數(shù)組時(shí)铆惑,進(jìn)行下面轉(zhuǎn)化。
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
   

由上面三種構(gòu)造方法可知送膳,默認(rèn)情況下使用ArrayList會(huì)生成一個(gè)大小為10的Object類型的數(shù)組员魏。也可以調(diào)用ArrayList(int initialCapacity) 來初始化Object數(shù)組的大小。并且用戶可以往ArrayList中傳入一個(gè)容器只要這個(gè)容器是Collection類型的叠聋。調(diào)用ArrayList(Collection<? extends E> c)接口的時(shí)候會(huì)將容器數(shù)組化處理并將這個(gè)數(shù)組值賦給Object數(shù)組撕阎。

實(shí)例:

public static void main(String[] args) {
        ArrayList<Integer> list_2=new ArrayList<Integer>(20);
        //list_2中添加元素
        for(int i=0;i<10;i++)
            list_2.add(i);
            
        ArrayList<Integer> list_3=new ArrayList<Integer>(list_2);
        //輸出list_2中元素
        for(Integer a:list_2)
            System.out.print(a+" ");
        //輸出list_3中元素
        for(Integer a:list_3)
            System.out.print(a+" ");
    }
//輸出
/*
list_2 : 0 1 2 3 4 5 6 7 8 9 
-----------------------
list_3 : 0 1 2 3 4 5 6 7 8 9 
*/  

②indexOf(Object o)方法
功能:查找某個(gè)元素在ArrayList中第一次出現(xiàn)的位置。

  public int indexOf(Object o) {
  //ArrayList中的元素可以為null碌补,如果為null返回null的下標(biāo)
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
                    
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        //如果沒有找到對應(yīng)的元素返回-1虏束。
        return -1;
    }

對于indexof方法做幾點(diǎn)說明:ArrayList中可以存放null元素,indexof是返回elementData數(shù)組中值相同的首個(gè)元素的下標(biāo)厦章,indexof中比較方法是equals而equals是比較元素的值镇匀,因此必須對null單獨(dú)查找。如果未找到該元素則返回-1 袜啃。


public static void main(String[] args) {
        
        ArrayList<Integer> list=new ArrayList<Integer>();
        
        list.add(1);        
        list.add(2);        
        list.add(null);     
        list.add(2);
        list.add(3);
        
        System.out.println("null: "+list.indexOf(null));
        System.out.println("-------------------------");
        System.out.println("2: "+list.indexOf(2));
        System.out.println("-------------------------");
        System.out.println("4: "+list.indexOf(4));
    }
    //輸出
    /*
    null: 2
    -------------------------
    2: 1
    -------------------------
    4: -1
    */

③lastIndexOf(Object o)方法
功能:查找某個(gè)元素在ArrayList中最后出現(xiàn)的位置汗侵。

public int lastIndexOf(Object o) {
        if (o == null) {
        //如果o為null從后往前找到第一個(gè)為null的下標(biāo)
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
        //從后往前找到第一個(gè)值為o的下標(biāo)
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

上面代碼做幾點(diǎn)說明:lastIndexOf(Object o)在ArrayList中從后往前找到第一個(gè)跟要查找值相同的元素的下標(biāo),因?yàn)槭前粗挡檎宜詫τ?null 要單獨(dú)查找囊骤。如果未找到則返回-1晃择;


④get(int index)方法
功能:返回ArrayList中指定下標(biāo)為index的元素。

public E get(int index) {
 //檢查index的值是否大于ArrayList的大小
        rangeCheck(index);
 //返回index下標(biāo)的元素       
        return elementData(index);
    }
    
   E elementData(int index) {
        return (E) elementData[index];
    }  
 //這里值檢查index >= size的情況也物,因?yàn)閕ndex<0時(shí)會(huì)自動(dòng)拋出異常宫屠,所以并未檢查index<0的情況。
 private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }      

對上面代碼做幾點(diǎn)說明:上面代碼中只檢查了index>=size的情況滑蚯,在index<0的情況下也會(huì)拋出異常浪蹂,只是這個(gè)異常是由系統(tǒng)拋出的抵栈。index>=size要檢查的原因是有可能數(shù)組的大小大于index,然而有效里面的元素<index這時(shí)不拋異常就會(huì)返回?zé)o效值坤次。舉個(gè)例子ArrayList的初始化大小為10古劲,現(xiàn)在往里面放5個(gè)元素,如果index>=5時(shí)缰猴,應(yīng)該要拋出異常产艾,而不是返回 null。因?yàn)閚ull 是可以主動(dòng)放在ArrayList中的滑绒。


⑤set(int index, E element)方法
功能:將element放到ArrayList下標(biāo)為index的位置闷堡,如果index<0或index>=size 拋異常,set(int index, E element)只能覆蓋ArrayList中原來的元素疑故,返回值為被覆蓋的元素杠览。

//1
public E set(int index, E element) {
//檢查index是否小于size,如果不是拋異常
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        //覆蓋ArrayList中index上的元素纵势。
        return oldValue;
        //返回被覆蓋的元素踱阿。
    }
//2    
 private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

⑥add(E e)方法
功能:往ArrayList中添加元素。

//1-----------------------
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 加入元素前檢查數(shù)組的容量是否足夠
        elementData[size++] = e;
        return true;
    }
//2----------------------- 
private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // 如果添加元素后大于當(dāng)前數(shù)組的長度钦铁,則進(jìn)行擴(kuò)容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    } 
//3-----------------------  
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //將數(shù)組的長度增加原來數(shù)組的一半软舌。
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
            //如果擴(kuò)充一半后仍然不夠,則 newCapacity = minCapacity;minCapacity實(shí)際元素的個(gè)數(shù)牛曹。
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
            //數(shù)組最大位2^32
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }     

add方法比較復(fù)雜葫隙,涉及到擴(kuò)充數(shù)組容量的問題。其中要弄清楚size和elementData.length的區(qū)別躏仇,size指的是數(shù)組中存放元素的個(gè)數(shù),elementData.length表示數(shù)組的長度腺办,當(dāng)new一個(gè)ArrayList系統(tǒng)默認(rèn)產(chǎn)生一個(gè)長度為10的elementData數(shù)組焰手,elementData.length=10,但是由于elementData中還未放任何元素所有size=0怀喉。如果加入元素后數(shù)組大小不夠會(huì)先進(jìn)行擴(kuò)容书妻,每次擴(kuò)容都將數(shù)組大小增大一半比如數(shù)組大小為10一次擴(kuò)容后的大小為10+5=10;ArrayList的最大長度為 2^32 .


⑦add(int index, E element)方法
功能:往ArrayList指定index上添加元素,添加元素后ArrayList的大小增1躬拢。index及以后的元素都會(huì)向后移一位躲履。

//1-------------------------
public void add(int index, E element) {
        rangeCheckForAdd(index);
//檢查index的值是否在0到size之間,可以為size聊闯。
        ensureCapacityInternal(size + 1);  // 看elementData的長度是否足夠工猜,不夠擴(kuò)容
 //將elementData從index開始后面的元素往后移一位。       
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
//2-------------------------
private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
 //3-------------------------  
 private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

add(int index, E element)往指定index中加入元素菱蔬,加入元素之前先檢查數(shù)組的大小篷帅,如果小了在原來基礎(chǔ)上增大一半史侣,將ArrayList只能怪index及以后的元素往后移一位,將element放到index位置魏身。


⑧remove(int index)方法
功能:刪除ArrayList指定位置的元素惊橱。

public E remove(int index) {
        rangeCheck(index);
//如果index>=size拋出異常
        modCount++;
        E oldValue = elementData(index);
//獲取刪除元素的值
        int numMoved = size - index - 1;
        //將index后面所有的元素往前移一位。
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // Let gc do its work
//返回要?jiǎng)h除的原數(shù)箭昵。
        return oldValue;
    }


⑨remove(Object o)方法
功能:刪除ArrayList中值為o的元素

public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末税朴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子家制,更是在濱河造成了極大的恐慌正林,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慰丛,死亡現(xiàn)場離奇詭異卓囚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诅病,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門哪亿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贤笆,你說我怎么就攤上這事蝇棉。” “怎么了芥永?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵篡殷,是天一觀的道長。 經(jīng)常有香客問我埋涧,道長板辽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任棘催,我火速辦了婚禮劲弦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘醇坝。我一直安慰自己邑跪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布呼猪。 她就那樣靜靜地躺著画畅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宋距。 梳的紋絲不亂的頭發(fā)上轴踱,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音乡革,去河邊找鬼寇僧。 笑死摊腋,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘁傀。 我是一名探鬼主播兴蒸,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼细办!你這毒婦竟也來了橙凳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對情侶失蹤笑撞,失蹤者是張志新(化名)和其女友劉穎岛啸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茴肥,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坚踩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓤狐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞬铸。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖础锐,靈堂內(nèi)的尸體忽然破棺而出嗓节,到底是詐尸還是另有隱情,我是刑警寧澤皆警,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布拦宣,位于F島的核電站,受9級(jí)特大地震影響信姓,放射性物質(zhì)發(fā)生泄漏鸵隧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一意推、第九天 我趴在偏房一處隱蔽的房頂上張望掰派。 院中可真熱鬧,春花似錦左痢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至描扯,卻和暖如春定页,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绽诚。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國打工典徊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杭煎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓卒落,卻偏偏與公主長得像羡铲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子儡毕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348