java 集合(一)——ArrayList

概念

ArrayList就是一個底層是數(shù)組形式組成的有序集合,允許重復(fù)數(shù)據(jù)备蚓,允許數(shù)據(jù)為null,但是非線程安全砌创,讓我們看看源碼

  public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

我們看到ArrayList實現(xiàn)了RondomAccess接口蚕钦,這也就意味著該類支持快速隨機訪問

以下是ArrayList的主要成員變量

    private static final int DEFAULT_CAPACITY = 10;//默認(rèn)初始容量為10
    private static final Object[] EMPTY_ELEMENTDATA = {};//定義一個空數(shù)組實例
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    private transient Object[] elementData;//存放數(shù)據(jù)的數(shù)組
    private int size;//數(shù)組的大小

注意一下,elementData是由transient修飾的逻恐,ArrayList繼承了Serializable,實現(xiàn)了序列化,elementData是一個緩存數(shù)組,它通常會預(yù)留一些容量峻黍,在很多時候复隆,這個數(shù)組都有很多空的數(shù)據(jù),所以如果存進(jìn)內(nèi)存姆涩,會占用多余的內(nèi)存空間挽拂,故使用transient反序列化。

創(chuàng)建

當(dāng)我們new一個ArrayList時骨饿,有三種構(gòu)造方式:

1.無參構(gòu)造器亏栈,默認(rèn)創(chuàng)建一個空的數(shù)組

 public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

當(dāng)添加了第一條數(shù)據(jù)后台腥,elementData初始容量為10;
2.指定集合的大小

  public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }

如圖仑扑,指定初始容量時览爵,elementData的大小為指定值,當(dāng)我們知道集合所需容量大小時镇饮,可以在創(chuàng)建集合時直接指定它的大小

3.構(gòu)造一個包含指定集合的Arraylist

   public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size,   Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
添加

先讓我們看一段代碼

    ArrayList<String> list = new ArrayList<>();
    list.add("hello");
    list.add("world");

讓我們看一下源碼蜓竹,看看底層是怎么做的

  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 索引增加
        elementData[size++] = e;
        return true;
    }

ensureCapacityInternal()是擴容方法,先不管储藐,實際添加數(shù)據(jù)就是直接在elementData數(shù)組的后面添加了數(shù)據(jù)俱济。

那么看看擴容的操作

  private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
  private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
 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);
    }
  private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

首先判斷當(dāng)前數(shù)組是否為空,若當(dāng)前數(shù)組不為空钙勃,且當(dāng)前數(shù)組的長度小于插入數(shù)據(jù)之后的長度蛛碌,則調(diào)用grow()方法進(jìn)行擴容,我們從int newCapacity = oldCapacity + (oldCapacity >> 1)可以看出辖源,擴容之后的容量為之前的1.5倍蔚携,然后進(jìn)行Arrays.copy()操作將之前的數(shù)組中的數(shù)據(jù)復(fù)制到新數(shù)組。

刪除

我們一般常用到的刪除方式分別是指定下標(biāo)刪除克饶,和指定內(nèi)容刪除酝蜒,讓我們看看源代碼是怎么做的

   /*
     * 通過制定下標(biāo)刪除
     */
   public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        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

        return oldValue;
    }
    /*
     * 通過指定對象刪除
     */
  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;
    }
    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
    }

這兩種刪除的方式都差不多,都是要把指定元素后面位置的所有元素矾湃,利用System.arraycopy方法整體向前移動一個位置亡脑,最后一個位置的元素指定為null,這樣讓gc可以去回收它

插入

    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++;
    }

插入元素的時候邀跃,也是要利用System.arraycopy()整體往后移霉咨,將元素放在指定位置,數(shù)組的大小增1

總結(jié)

ArrayList底層是用數(shù)組實現(xiàn)的拍屑,具有動態(tài)擴容特性途戒。初始容量為10,擴容增量為原容量的1.5
ArrayList具有隨機訪問特性丽涩,查找速度快
ArrayList插入和刪除操作效率比較低棺滞,因為涉及到元素復(fù)制,當(dāng)需要復(fù)制的元素過多時矢渊,比較耗費性能继准。
因此,ArrayList比較適合順序添加矮男、隨機訪問的場景移必。

小插曲:System.arraycopy()和Arrays.copyOf()的方法的區(qū)別?

以下是它們的源碼:

   /*
     * System.arraycopy
     */
  public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
   /*
     * Arrays.copyOf
     */
  public static short[] copyOf(short[] original, int newLength) {
        short[] copy = new short[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

arraycopy()是native方法毡鉴,所以看不到arraycopy()的具體實現(xiàn)崔泵,哭(???)
我們看到Arrays.copy()的最后還是調(diào)用了System.arraycopy(),Arrays.copy()限制了復(fù)制的范圍只能是整個數(shù)組

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秒赤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子憎瘸,更是在濱河造成了極大的恐慌入篮,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幌甘,死亡現(xiàn)場離奇詭異潮售,居然都是意外死亡,警方通過查閱死者的電腦和手機锅风,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門酥诽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人皱埠,你說我怎么就攤上這事肮帐。” “怎么了边器?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵训枢,是天一觀的道長。 經(jīng)常有香客問我忘巧,道長肮砾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任袋坑,我火速辦了婚禮,結(jié)果婚禮上眯勾,老公的妹妹穿的比我還像新娘枣宫。我一直安慰自己,他們只是感情好吃环,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布也颤。 她就那樣靜靜地躺著,像睡著了一般郁轻。 火紅的嫁衣襯著肌膚如雪翅娶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天好唯,我揣著相機與錄音竭沫,去河邊找鬼。 笑死骑篙,一個胖子當(dāng)著我的面吹牛蜕提,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播靶端,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼谎势,長吁一口氣:“原來是場噩夢啊……” “哼凛膏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脏榆,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤猖毫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后须喂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吁断,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年镊折,在試婚紗的時候發(fā)現(xiàn)自己被綠了胯府。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡恨胚,死狀恐怖骂因,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赃泡,我是刑警寧澤寒波,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站升熊,受9級特大地震影響俄烁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜级野,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一页屠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蓖柔,春花似錦辰企、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至镐捧,卻和暖如春潜索,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背懂酱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工竹习, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人列牺。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓由驹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蔓榄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

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