【CSDN博客遷移】Java核心技術(shù)筆記——數(shù)據(jù)結(jié)構(gòu)(2)

上一篇中的提到集合具體實(shí)現(xiàn)類在后續(xù)章節(jié)中逐一分析亏吝,本篇來(lái)分析項(xiàng)目中經(jīng)常用到的數(shù)組列表(ArrayList)

1 數(shù)組列表類關(guān)系

數(shù)組列表UML.png

ArrayList主要實(shí)現(xiàn)了List熊响、RandomAccess秒梅、Cloneable刀森、Serializable接口撮奏,繼承了AbstractList抽象類杠览。
List接口定義了數(shù)組列表必須實(shí)現(xiàn)的方法
AbstractList實(shí)現(xiàn)了List中的通用的方法弯菊;
RandomAccess接口上一篇提到過(guò),是為了監(jiān)測(cè)列表查詢效率踱阿,做個(gè)標(biāo)記管钳;
Cloneable接口可以實(shí)現(xiàn)對(duì)象的克露趾贰;
Serializable接口標(biāo)識(shí)序列號(hào)蹋嵌;

2 數(shù)組列表(ArrayList)源碼分析

2.1 屬性字段和構(gòu)造函數(shù)

 /**
 * Integer.MAX_VALUE是int整形的最大取值2的31次方-1育瓜,這個(gè)常量用來(lái)控制集合的最大容量
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
 * 存放ArrayList對(duì)象的數(shù)組
 */
private transient Object[] elementData;

/**
 * 數(shù)組列表大小
 */
private int size;

/**
 * 根據(jù)傳入的數(shù)字初始化elementData 數(shù)組
 */
public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
}

/**
 * 默認(rèn)elementData 的大小為10
 */
public ArrayList() {
    this(10);
}

/**
 * 根據(jù)傳入的集合構(gòu)造數(shù)組列表
 */
public ArrayList(Collection<? extends E> c) {
    //將傳入的集合轉(zhuǎn)換為數(shù)組,賦值給elementData 
    elementData = c.toArray();
    size = elementData.length;
    // 如果elementData 的元素類型不是Object栽烂,通過(guò)Arrays.copyOf轉(zhuǎn)換為Object
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, size, Object[].class);
    }

2.2 添加元素

覆蓋了AbstractList中的add()方法躏仇,這里有個(gè)問(wèn)題,當(dāng)超出列表的長(zhǎng)度時(shí)腺办,如何自動(dòng)增加列表長(zhǎng)度焰手,添加元素,詳細(xì)看下面你的源碼分析:

 /**
 * 添加元素到列表
 */
public boolean add(E e) {
    //確認(rèn)列表容量方法
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

/**
 * 當(dāng)添加元素超過(guò)容器的容量時(shí)怀喉,自動(dòng)擴(kuò)增容器容量
 */
private void ensureCapacityInternal(int minCapacity) {
    modCount++;
    // 注意這里實(shí)現(xiàn)了自動(dòng)擴(kuò)增容器容量
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

 /**
 * 自動(dòng)擴(kuò)增容器容量方法
 */
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    
    /**
     * 假設(shè)以空參數(shù)方法構(gòu)造列表书妻,則容器默認(rèn)大小是10,當(dāng)往列表中加載第11個(gè)元
     * 素時(shí)躬拢,也就是說(shuō)oldCapacity =11躲履,11的二進(jìn)制單位右移1位,是0000 0101聊闯,
     * 十進(jìn)制是5工猜,即newCapacity =15,這里把容器的長(zhǎng)度擴(kuò)增了5位.
    */
   
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //如果newCapacity <minCapacity菱蔬,newCapacity 取最小值minCapacity
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //如果newCapacity >MAX_ARRAY_SIZE 篷帅,newCapacity 取最大值
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //調(diào)整elementData 的大小為新的容量
    elementData = Arrays.copyOf(elementData, newCapacity);
}

 private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    //Integer.MAX_VALUE是int整形的最大取值2的31次方-1
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

 /**
 * 添加元素的集合指定位置
 */
public void add(int index, E element) {
   //檢查索引位置是否超出列表邊界
    rangeCheckForAdd(index);
    //確定是否要擴(kuò)增
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //將elementData數(shù)組中index的位置空留出來(lái)
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //將新插入的element賦給elementData數(shù)組index位置的值
    elementData[index] = element;
    size++;
}

 /**
 * 檢查索引位置是否超出列表邊界
 */
private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

/**
 * 添加集合到列表
 */
public boolean addAll(Collection<? extends E> c) {
    //轉(zhuǎn)換引入的集合(c)為數(shù)組
    Object[] a = c.toArray();
    //獲取引入集合的數(shù)組大小
    int numNew = a.length;
    //確定是否要擴(kuò)增
    ensureCapacityInternal(size + numNew);  // Increments modCount
    //拷貝引入的數(shù)組到elementData的尾部
    System.arraycopy(a, 0, elementData, size, numNew);
    //是列表的長(zhǎng)度增加為size+numNew
    size += numNew;
    //引入的集合(c)大小不為空,則返回true
    return numNew != 0;
}

/**
 * 添加集合到列表指定位置
 */
public boolean addAll(int index, Collection<? extends E> c) {
    //同上
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    //根據(jù)傳入的集合拴泌,確定是否需要擴(kuò)增容量
    ensureCapacityInternal(size + numNew);  // Increments modCount
    
    int numMoved = size - index;
    if (numMoved > 0)
        //先將elementData數(shù)組中index到index + numNew 值后移魏身,為引入c集合空出位置
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    //將c集合中元素復(fù)制到elementData數(shù)組中index到index + numNew的位置
    System.arraycopy(a, 0, elementData, index, numNew);
    //同上
    size += numNew;
    return numNew != 0;
}

2.3 移除元素

下面分析如何動(dòng)態(tài)的移除數(shù)列表中的元素

/**
 * 根據(jù)指定的索引移除元素
 */
public E remove(int index) {
    rangeCheck(index);
    //注意modCount繼承自AbstractList抽象類,是一個(gè)不包括在序列化中的值
    modCount++;
    // 訪問(wèn)elementData[index]值
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        //將elementData數(shù)組的元素從index+1位置后移到index
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //將elementData數(shù)組最后一個(gè)位置元素設(shè)置為null
    elementData[--size] = null; // Let gc do its work
    //返回被移除的元素
    return oldValue;
}   

/**
 * 判斷移除元素的位置是否超出邊界
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 返回elementData[index]值
@SuppressWarnings("unchecked")
E elementData(int index) {
    return (E) elementData[index];
    
/**
 * 根據(jù)傳入的對(duì)象蚪腐,去列表中循環(huán)查詢箭昵,如果存在相同對(duì)象,調(diào)用fastRemove方法移除
 */
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;
}

/*
 * 根據(jù)位置index移除元素削茁,和remove(int index) 方法的區(qū)別宙枷,是不用返回移除元素值
 */
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; // Let gc do its work
}

2.3 訪問(wèn)元素

/**
 * 隨機(jī)訪問(wèn)數(shù)組列表中的元素
 */
public E get(int index) {
    //判斷index是否超出數(shù)組列表訪問(wèn)
    rangeCheck(index);
    //放回elementData[index]值,這里可以說(shuō)明茧跋,ArrayList隨機(jī)訪問(wèn)的效率高
    return elementData(index);
}

2.4其他方法

2.4.1 包含運(yùn)算

/**
 * 暴露給外部的集合包含運(yùn)算方法
 */
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

/**
 *實(shí)現(xiàn)集合包含運(yùn)算
 */
public int indexOf(Object o) {
    if (o == null) {
       //如果o是null慰丛,循環(huán)elementData,查找是否有null值
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
    //如果o不是是null瘾杭,循環(huán)elementData诅病,查找是否有相等的值,
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    //有相等的值返回索引i,否則返回-1贤笆;
    return -1;
}

/**
 * 顧名思義和上面的相反蝇棉,反向查詢是否相等的值
 */
public int lastIndexOf(Object o) {
    if (o == null) {
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

2.4.2 求交運(yùn)算

 /**
 * 暴露給外部的集合求交集方法
 */
public boolean retainAll(Collection<?> c) {
    return batchRemove(c, true);
}

//實(shí)現(xiàn)交集運(yùn)算,
private boolean batchRemove(Collection<?> c, boolean complement) {
    //將elementData數(shù)組轉(zhuǎn)換為常量數(shù)組
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        for (; r < size; r++)
           //循環(huán)訪問(wèn)列表芥永,調(diào)用contains方法篡殷,判斷引入的集合是否包含列表中的元素,如果有賦值給新的常量數(shù)組
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

參考文章

1 《Java核心技術(shù)(卷一第9版)》
2 http://blog.csdn.net/jzhf2012/article/details/8540410

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末埋涧,一起剝皮案震驚了整個(gè)濱河市板辽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棘催,老刑警劉巖劲弦,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異醇坝,居然都是意外死亡邑跪,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)呼猪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)画畅,“玉大人,你說(shuō)我怎么就攤上這事郑叠∫拐裕” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵乡革,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我摊腋,道長(zhǎng)沸版,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任兴蒸,我火速辦了婚禮视粮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘橙凳。我一直安慰自己蕾殴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布岛啸。 她就那樣靜靜地躺著钓觉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪坚踩。 梳的紋絲不亂的頭發(fā)上荡灾,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼批幌。 笑死础锐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荧缘。 我是一名探鬼主播皆警,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼截粗!你這毒婦竟也來(lái)了信姓?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桐愉,失蹤者是張志新(化名)和其女友劉穎财破,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體从诲,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡左痢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了系洛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俊性。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖描扯,靈堂內(nèi)的尸體忽然破棺而出定页,到底是詐尸還是另有隱情,我是刑警寧澤绽诚,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布典徊,位于F島的核電站,受9級(jí)特大地震影響恩够,放射性物質(zhì)發(fā)生泄漏卒落。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一蜂桶、第九天 我趴在偏房一處隱蔽的房頂上張望儡毕。 院中可真熱鬧,春花似錦扑媚、人聲如沸腰湾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)费坊。三九已至,卻和暖如春押桃,著一層夾襖步出監(jiān)牢的瞬間葵萎,已是汗流浹背导犹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留羡忘,地道東北人谎痢。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像卷雕,于是被迫代替她去往敵國(guó)和親节猿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容