ArrayList源碼淺析

ArrayList源碼淺析

概述

ArrayList是底層結構爲數組,長度可變的有序集合java

位置

java.util.ArrayList數組

類定義

ClassDefine.png
繼承AbstractList
實現List、RandomAccess、Cloneable、Serializable併發

  • AbstractList
  • List
  • RandomAccess
  • Cloneable
  • Serializable

1.AbstractList
    _做用:簡單實現List中的方法_
2.List
     爲何實現兩邊List?dom

static interface Subject{
        void sayHi();
        void sayHello();
    }
    static class AbstractSubject implements Subject{
        @Override
        public void sayHi() {
            System.out.println("hi");
        }

        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }
    static class SubjectImpl extends AbstractSubject /*implements Subject*/{

        @Override
        public void sayHi() {
            System.out.println("hi");
        }

        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }
    static class ProxyInvocationHandler implements InvocationHandler {
        private Subject target;
        public ProxyInvocationHandler(Subject target) {
            this.target=target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.print("say:");
            return method.invoke(target, args);
        }

    }
    public static void main(String[] args) {
        Subject subject=new SubjectImpl();
        Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
        subjectProxy.sayHi();
        subjectProxy.sayHello();
    }
    
    結果:
    Exception in thread "main" java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to ProxyTest$Subject
        at ProxyTest.main(ProxyTest.java:51)

3.RandomAccess
    _做用:使其支持快速隨機訪問_ide

/**
 * 支持ArrayList支持快速隨機查詢,使用普通for時效率更高
 */
public static void test13(){
    ArrayList<String> arrayList = new ArrayList<>();
    for (Integer i = 0; i < 1000000; i++) {
        arrayList.add("1");
    }
    long arrayStart = System.currentTimeMillis();
    for (String s : arrayList) {
    }
    System.out.println("===forEach:"+(System.currentTimeMillis()-arrayStart));
    long arrayStart2 = System.currentTimeMillis();
    for (int i = 0; i < arrayList.size(); i++) {
    }
    System.out.println("===for:"+(System.currentTimeMillis()-arrayStart2));
 }
        
運行結果:
===forEach:16
===for:5

    java.util.Collections中的應用
collections.png
爲何LinkedList不實現呢?
我的淺析:不是由於加了這個標記接口使for循環效率變高,而是加了這個接口可讓其餘工具類用效率最高的方式處理被標記的類 函數

4.Cloneable
5.Serializable工具

成員屬性

//序列化ID
private static final long serialVersionUID = 8683452581122892189L;
//默認容量
private static final int DEFAULT_CAPACITY = 10;
//共享空數組
private static final Object[] EMPTY_ELEMENTDATA = {};
//默認空數組,只有在調用默認構造函數時會使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存放數據數據
transient Object[] elementData;
//數組內實際元素個數
private int size;
//數組最大元素數
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;this

transient
短暫的; 轉瞬即逝的; 臨時的

被其修飾的字段在序列化過程當中將被忽略,若是須要對其作特殊操做,能夠定義writeObject和readObject來進行實現特殊的序列化和反序列化
readObj.pngspa

writeObj.png
既不是重寫父類方法,也不是實現接口方法,如何發揮做用?
java.io.ObjectInputStream.readSerialData
java.io.ObjectOutputStream.writeSerialData
序列化和反序列化時會調用這兩個方法,方法中判斷被操做對象中是否存在這兩個方法設計

構造方法

//自定義初始容量
public ArrayList(int initialCapacity)
//默認構造方法
public ArrayList()
//將傳入集合轉爲ArrayList
public ArrayList(Collection<? extends E> c)
construction.png

Test Method:

static void test09(){
        try {
            List<Integer> integers = Arrays.asList(1, 2, 3);
            Object[] asObjs = integers.toArray();
            asObjs[2] = "123";
        }catch (Exception e) { //java.lang.ArrayStoreException: java.lang.String
            e.printStackTrace();
        }

        try {
            List<String> arrayList = new ArrayList<>();
            arrayList.add("123");
            arrayList.add("123");
            arrayList.add("123");
            Object[] arrayObjs = arrayList.toArray();
            arrayObjs[2] = 123;
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

核心方法

//確認容量
//若是必要,增長此ArrayList實例的容量,以確保它至少能夠容納由minimum capacity參數指定的元素數。
public void ensureCapacity(int minCapacity)
//確認容量內部
private void ensureCapacityInternal(int minCapacity)
//確認清楚的容量
private void ensureExplicitCapacity(int minCapacity
//擴容
//增長容量以確保它至少能夠保存最小容量參數指定的數量的元素
private void grow(int minCapacity)

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //將容量擴容至原容量的1.5倍
        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;
    }

Test Code:

static void test10(){
             int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8;
             int minCapacity = Integer.MAX_VALUE+1;
             // overflow-conscious code
             int oldCapacity = Integer.MAX_VALUE;//換個較小的數11
             
             int newCapacity = oldCapacity + (oldCapacity >> 1);
             if (newCapacity - minCapacity < 0)
                 newCapacity = minCapacity;
             if (newCapacity - MAX_ARRAY_SIZE > 0)
                 newCapacity = hugeCapacity(minCapacity);
                 
             System.out.println(newCapacity);
         }
         private static int hugeCapacity(int minCapacity) {
             int MAX_ARRAY_SIZE = Integer.MAX_VALUE-8;
             if (minCapacity < 0) // overflow
                 throw new OutOfMemoryError();
             return (minCapacity > MAX_ARRAY_SIZE) ?
                     Integer.MAX_VALUE :
                     MAX_ARRAY_SIZE;
         }
         
    結果:拋出異常

經常使用方法

//是否包含指定元素
public boolean contains(Object o) 
//返回指定元素下標
public int indexOf(Object o)
//獲取指定位置元素
public E get(int index)
//替換指定下標元素
public E set(int index, E element) 
//在集合末尾添加元素
public boolean add(E e)
//在集合指定位置添加元素
public void add(int index, E  element)
//將另外一個集合追加至指定集合末尾
public boolean addAll(Collection<? extends E> c)
//將另外一個集合插入指定集合位置
public boolean addAll(int index, Collection<? extends E> c)
//移除指定下標元素
public E remove(int index)
//移除指定元素
public boolean remove(Object o)
//移除入參數組中在原集合相同的元素
public boolean removeAll(Collection<?> c)
//保留入參數組中在原集合相同的元素
public boolean retainAll(Collection<?> c)

remove

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

這個方法能夠看出邏輯仍是比較簡單,大概就是將要剔除元素後面的元素總體賦值到要剔除元素之後,而後在降最後一位置空

batchRemove

private boolean batchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;//r表示正常循環原始數組的下標位置;w表示符合要求的元素將要存放的位置(即記錄要覆蓋的元素位置)
    boolean modified = false;
    try {
        for (; r < size; r++)
            //判斷當前遍歷到的原數組元素是否知足入參數組中元素(存在或不存在)
            //其實就是找到想要的元素
            if (c.contains(elementData[r]) == complement)
                //知足則將當前元素複製到「結果列表」的最後一位
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        //若是發生異常,將已經檢查位置以後的(r下標之後的)元素,放到「結果列表」w的位置上
        if (r != size) {
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        //原集合是否發生變更,發生則刪除無用元素
        if (w != size) {
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

若是使用正向邏輯
    for (; r < size; r++)
        if (c.contains(elementData[r]) == complement) {
        } else {
            elementData[w++] = elementData[r];
        }

批量修改代碼如上,能夠看出來並非簡單的循環調用remove,而是採用了新的方法,代碼看起來可能比較枯燥,下面是我發揮我靈魂畫手的能力,畫了一個步驟圖。
batchRemove.png
他的總體思路也是將有用元素提早,並在結束之後將無用元素置空

迭代器

private class Itr implements Iterator<E>

併發修改異常

當使用迭代器遍歷集合,在此過程當中對集合中元素進行修改則會拋出
對於刪除元素,可使用ArrayList迭代器中實現的remove方法進行刪除

加強for中刪除元素必定會拋併發修改異常嗎?
不必定,當刪除的元素位於集合的倒數第二位時,不會拋異常

public static void test14(){
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    for (Integer integer : list) {
        if (3 == integer) {
            list.remove(integer);
        }
    }
    System.out.println(list);//[1, 2, 4]
}

屬性

  • int cursor;//要返回的下一個元素的索引(遊標)
  • int lastRet = -1; //返回最後一個元素的索引; -1若是沒有這樣的話
  • int expectedModCount = modCount;

方法

  • public boolean hasNext()
  • public E next()
  • public void remove()

fail-fast

在系統設計中,快速失效系統一種能夠當即報告任何可能代表故障的狀況的系統。快速失效系統一般設計用於中止正常操做,而不是試圖繼續可能存在缺陷的過程。這種設計一般會在操做中的多個點檢查系統的狀態,所以能夠及早檢測到任何故障。快速失敗模塊的職責是檢測錯誤,而後讓系統的下一個最高級別處理錯誤。

最後

仍是一如既往感謝你們的閱讀,若是以爲寫得還湊活的話,不妨點個贊哈~

相關文章
相關標籤/搜索