1. 程式人生 > >ArrayList和CopyOnWriteArrayList面試題總結與原始碼分析

ArrayList和CopyOnWriteArrayList面試題總結與原始碼分析

面試題

(1)ArrayList和CopyOnWriteArrayList的增刪改查實現原理?

(2)為什麼說ArrayList查詢快而增刪慢?

(3)弱一致性的迭代器原理是怎麼樣的?

(4)CopyOnWriteArrayList為什麼併發安全且效能比Vector好?

(1)ArrayList和CopyOnWriteArrayList的增刪改查實現原理?

ArrayList類圖

CopyOnWriteArrayList類圖

1.List介面

首先我們來看List介面,如上因為ArrayList和CopyOnWriteArrayList都是實現了List介面,所有檢視其相應的方法即可。

2.ArrayList的幾個重點

1、底層是陣列,初始化大小為10

2、插入時會判斷陣列容量是否足夠,不夠的化會進行擴容

3、所謂擴容就是新建一個數組,然後將老的資料裡面的元素複製到新的數組裡面

4、移除元素的時候也涉及到陣列中元素的移動,刪除指定index位置的元素,然後將index+1至陣列最後一個元素往前移動一格

2.2增刪改查

1.增

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!! //——進行陣列擴容,不夠就擴容
    elementData[size++] = e;
    return true;
}

/**
 * Inserts the specified element at the specified position in this
 * list. Shifts the element currently at that position (if any) and
 * any subsequent elements to the right (adds one to their indices).
 *
 * @param index index at which the specified element is to be inserted
 * @param element element to be inserted
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public void add(int index, E element) {
    rangeCheckForAdd(index); //——檢查是否越界

    ensureCapacityInternal(size + 1);  // Increments modCount!! //——進行陣列容量判斷,不夠就擴容
    System.arraycopy(elementData, index, elementData, index + 1, //——將index至資料最後一個元素整體往後移動一格,然後插入新的元素
                     size - index);
    elementData[index] = element;
    size++;
}

2.刪

/**
 * Removes the element at the specified position in this list.
 * Shifts any subsequent elements to the left (subtracts one from their
 * indices).
 *
 * @param index the index of the element to be removed
 * @return the element that was removed from the list
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E remove(int index) {
    rangeCheck(index); //——判斷是否越界

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

    int numMoved = size - index - 1;
    if (numMoved > 0) //——若該元素不是最後一個元素的話,將index+1至陣列最後一個元素整體向前移動一格
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

/**
 * Removes the first occurrence of the specified element from this list,
 * if it is present.  If the list does not contain the element, it is
 * unchanged.  More formally, removes the element with the lowest index
 * <tt>i</tt> such that
 * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>
 * (if such an element exists).  Returns <tt>true</tt> if this list
 * contained the specified element (or equivalently, if this list
 * changed as a result of the call).
 *
 * @param o element to be removed from this list, if present
 * @return <tt>true</tt> if this list contained the specified element
 */
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 remove method that skips bounds checking and does not
 * return the value removed.
 */
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
}

3.改

public E set(int index, E e) {
    rangeCheck(index);
    checkForComodification();
    E oldValue = ArrayList.this.elementData(offset + index);
    ArrayList.this.elementData[offset + index] = e; //——將陣列對應的index的元素進行替換
    return oldValue;
}

4.查

public E get(int index) {
    rangeCheck(index); //——進行陣列越界判斷
    checkForComodification();
    return ArrayList.this.elementData(offset + index); //——獲取陣列對應的index元素
}
E elementData(int index) {
    return (E) elementData[index];
}

總結

(2)為什麼說ArrayList查詢快而增刪慢?

以上部分就是ArrayList的增刪改查原理,以此就可以解決我們的第二個問題了。

ArrayList底層是陣列,所以查詢的時候直接根據索引可以很快地找到對應地元素,改也是如此,找到index對應元素進行替換。而增加和刪除就涉及到陣列元素地移動,所以會比較慢。

CopyOnWriteArrayList幾個關鍵點

1、實現了List介面

2、內部持有一個ReentrantLock lock=new ReentrantLock()

3、底層是用volatile transient宣告地陣列 array

4、讀寫分離,寫時複製出一個新地陣列,完成插入、修改或者移除操作後將新陣列賦值給array

增刪改查

1.增

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //——獲得鎖