java 數(shù)據(jù)結(jié)構(gòu)

ArrayList,List 接口,是順序容器,即元素存放的數(shù)據(jù)與放進(jìn)去的順序相同,允許放入null元素,底層通過數(shù)組實(shí)現(xiàn)。

   transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

可以看到成員變量為一個(gè)Object[] (Object數(shù)組),Object[] obj這樣的形式,就是一個(gè)Object數(shù)組構(gòu)成的參數(shù)形式。說明這個(gè)方法的參數(shù)是固定的,是一個(gè)Object數(shù)組,至于這個(gè)數(shù)組中存儲的元素,可以是繼承自O(shè)bject的所有類的對象。
至于 ‘ transien’關(guān)鍵詞,我也不太了解,只知道與反序列化有關(guān),這個(gè)暫時(shí)不做了解;
size代表這個(gè)鏈表所包含元素的個(gè)數(shù);

方法剖析:
1、get()方法
得到下標(biāo)為index的值;

   public E get(int index) {
        Objects.checkIndex(index, size);
        return elementData(index);
    }
   // cheackIndex 是判斷索引下標(biāo)是否超過了容器;

2、set() 方法

public E set(int index, E element) {
        Objects.checkIndex(index, size); //越界檢查
        E oldValue = elementData(index);
        elementData[index] = element; //可以看到復(fù)制的僅僅是引用;
        return oldValue;
    }

3、add() 方法

    private void add(E e, Object[] elementData, int s) {  
    //按照順序在尾部加入,故時(shí)間復(fù)雜度為O(1)
        if (s == elementData.length)
            elementData = grow(); //grow() 函數(shù)即為擴(kuò)容過程;
        elementData[s] = e;
        size = s + 1;
    }

  public void add(int index, E element) {   
//向指定位置增加元素,故時(shí)間復(fù)雜度為O(n),其它元素向后平移;
        rangeCheckForAdd(index);
        modCount++;
        final int s;
        Object[] elementData;
        if ((s = size) == (elementData = this.elementData).length)
            elementData = grow();
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = element;
        size = s + 1;
    }



-----------
    private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

    private Object[] grow() {
        return grow(size + 1); 
    }
   //擴(kuò)容后,數(shù)組長度加1;

  private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); 
        //可以看到,新的數(shù)組的長度為舊數(shù)組長度的1.5倍,右移運(yùn)算;
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }
--------- 
// copyof    arrays類
   public static int[] copyOf(int[] original, int newLength) {
        int[] copy = new int[newLength]; //創(chuàng)建了一個(gè)新的數(shù)組
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
-----
//systems類
  public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

所以ArrayList的擴(kuò)容方法如下:
1、數(shù)組內(nèi)增加一個(gè)元素,若超過其容器長度,則容器長度自動擴(kuò)大1.5倍,插入元素后,其數(shù)組長度加1;
2、過程如下圖:

自動擴(kuò)容
插入元素

[注圖片轉(zhuǎn)自 http://www.cnblogs.com/CarpenterLee/p/5419880.html]

4、addAll()方法
addAll()方法能夠一次添加多個(gè)元素,根據(jù)位置不同也有兩個(gè)把本,一個(gè)是在末尾添加的addAll(Collection<? extends E> c)方法,一個(gè)是從指定位置開始插入的addAll(int index, Collection<? extends E> c)方法。跟add()方法類似,在插入之前也需要進(jìn)行空間檢查,如果需要則自動擴(kuò)容(擴(kuò)容為,兩者和的 1.5倍);如果從指定位置插入,也會存在移動元素的情況。
addAll()的時(shí)間復(fù)雜度不僅跟插入元素的多少有關(guān),也跟插入的位置相關(guān)。

   public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        modCount++;
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew);

        int numMoved = s - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index,
                             elementData, index + numNew,
                             numMoved);
        System.arraycopy(a, 0, elementData, index, numNew);
        size = s + numNew;
        return true;
    }

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        modCount++;
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew); 
     //擴(kuò)容為,兩者和的 1.5倍;
        System.arraycopy(a, 0, elementData, s, numNew);
        size = s + numNew;
        return true;
    }

5、remove() 方法

  public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException(); //拋出異常
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

  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
    }

  public void clear() {   //清空
        modCount++;
        final Object[] es = elementData;
        for (int to = size, i = size = 0; i < to; i++)
            es[i] = null; // 每個(gè)都賦值為null
    }

關(guān)于Java GC這里需要特別說明一下,有了垃圾收集器并不意味著一定不會有內(nèi)存泄漏。對象能否被GC的依據(jù)是是否還有引用指向它,上面代碼中如果不手動賦null值,除非對應(yīng)的位置被其他元素覆蓋,否則原來的對象就一直不會被回收。

6、contains() 方法

  public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index {@code i} such that
     * {@code Objects.equals(o, get(i))},
     * or -1 if there is no such index.
     */
    public int indexOf(Object o) {    //時(shí)間復(fù)雜度為O(n)
        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;
        }
        return -1;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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