第一章第一篇、深入ArrayList源碼分析

ArrayList簡介

  1. 概念

    ArrayList是一個容量能夠動態增長的動態數組。和java的數組相比,它的容量能夠動態增長。它繼承了AbstractList,實現了List、RandomAccess、Cloneable、java.io.Serializable

  2. 繼承關係
    首先,我們先查看下ArrayList在java集合體中所在的位置:
    在這裏插入圖片描述
    在這裏我們可以看到ArrayList的根部是Collection,下面我們看分析下ArrayList的繼承關係在這裏插入圖片描述
    上圖清晰的描述到,實現四個接口繼承一個抽象類:

  • ArrayList繼承AbstractList;實現List。它是一個數組隊列,提供了新增、修改、刪除和遍歷等功能。
  • ArrayList實現了RandomAccess,即提供了隨機訪問功能。
  • ArrayList實現了Cloneable,即覆蓋了函數clone(),能夠被克隆。
  • ArrayList實現了java.io.Serializable,這表明ArrayList可以被序列化,能通過序列化去傳輸。
  • 和Vector不同,ArrayList操作的是線程不安全的,建議在單線程的情況下使用,如果是多線程的情況下建議使用Vector和CopyOnWriteArrayList
  • 從ArrayList源碼分析

1.ArrayList屬性
ArrayList屬性主要有當前數組長度size、存放數組對象elementData數組和代表ArrayList修改次數從AbstractList繼承的modCount屬性。

// 序列化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; // non-private to simplify nested class access
// 當前數組的長度
private int size;

2.ArrayList的構造方法
ArrayList構造方法有三種,分別是默認不傳參、初始化容量的空數組和包含元素的數組。

  • 無參構造函數,初始容量爲10的空數組,如下:
// 第一種,調用ArrayList(10),其中初始化的一個長度爲10的object數組
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • 初始化大小的構造函數,如下:
public ArrayList(int initialCapacity) {
    // 如果初始化大小小於0會報異常,否則新建初始化爲object的數組
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
  • 帶collection對象的構造方法,
    1)先將集合轉換成數組,然後賦值給elementData;
    2)將數組長度賦值給size並判斷是否爲0,
    如下:
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

1.0、這邊談一下ArrayList(Collection<? extends E> c)是深拷貝還是淺拷貝,測試代碼如下:

/** 測試ArrayList(Collection<? extends E> c)是淺拷貝還是深拷貝 **/
		List<BeanDemo> beanDemos = new ArrayList<>();
        BeanDemo beanDemo = new BeanDemo();
        beanDemo.setName("test1");
        beanDemos.add(beanDemo);
        List<BeanDemo> beanDemoList = new ArrayList<>(beanDemos);
        beanDemo.setName("test2");
        System.out.println(beanDemoList.get(0).getName());
        //結果輸出test2

1.2、深入研究一下源碼,爲什麼ArrayList(Collection<? extends E> c)構造函數是淺拷貝,其中調用了Arrays.copyof()方法,傳參是原始數組和拷貝數組的長度,copyof()通過獲取一個原始數組的副本,被截斷或用null填充以返回指定的長度。源碼如下,到這一步還看不出是到底是淺拷貝還是深拷貝:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

1.3、copyof()方法中主要使用System.arraycopy()方法來實現列表拷貝,再往下查看System.arraycopy(),發現這個是一個原生native的函數(native方法不是用java實現的),我只能仔細閱讀一下這個原生方法的註釋,參見文章最下方,註釋內容比較長,但第一段註釋直接告訴了我們arraycopy()方法拷貝的是數組的引用地址,所以它屬於淺拷貝:A subsequence of array components are copied from the source array referenced by src to the destination array referenced by dest.

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

2.0、爲何要判斷elementData.getClass() != Object[].class,當我們查看c.toArray()源碼的時候肯定認爲返回的就是Object[]數組,爲什麼還需要判斷呢?在查閱了各種資料之後才知道Arrays含有一個asList方法,Arrays.asList
體現的是適配器模式,只是轉換接口,後臺的數據仍是數組。

用代碼來證實下:

		List<String> llll = Arrays.asList("zhen", "xiang");
		System.out.println(llll.toArray().getClass());
		//返回結果:class [Ljava.lang.String;

到這ArrayList集合的三種構造方法就講完了。下一篇會繼續講解它的其他方法~~~
借鑑資料:
1.源碼分析:
https://blog.csdn.net/augfun/article/details/82323164
https://baijiahao.baidu.com/sid=1637926321175819771&wfr=spider&for=pc
2.判斷toArray()方法是否返回Object[]:
https://blog.csdn.net/weixin_43390562/article/details/101236699
3.判斷ArrayList(Collection<? extends E> c)是深拷貝還是淺拷貝:
https://blog.csdn.net/king0406/article/details/103752855

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章