ArrayList集合以及處理多線程不安全問題 (手寫)

手寫 | ArrayList集合

底層採用數組方式
怎樣保證集合存放無限大小—數組擴容技術

在這裏插入圖片描述

代碼實現:
package MyArraayList;

import java.util.Arrays;

/**
 * @author 孫一鳴 on 2020/3/3
 */
public class ExtArrayList {
    //定義一個數組,存放數據
    private Object[] elementData;

    //默認初始化數組容量大小
    private static final int DEFAULT_CAPACITY = 10;

    //空數組
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //記錄ArrayList大小
    private int size;

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    //無參構造,默認初始化容量爲10
    public ExtArrayList() throws Exception {
        //elementData = new Object[DEFAULT_CAPACITY];
        this(DEFAULT_CAPACITY);
    }

    //有參構造
    public ExtArrayList(int initialCapacity) throws Exception {
        if (initialCapacity < 0) {
            throw new Exception("初始化數組不能小於0");
        }
        this.elementData = new Object[initialCapacity];
    }

    public void add(int index, Object element) {

        //擴容
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //將index後面的對象,移動到index+1的位置上,將需要添加的值覆蓋
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        elementData[index] = element;
        size++;
    }

    public void add(Object object) {
        //如果給ArrayList添加對象,先判斷容量是否需要擴容
        ensureCapacityInternal(size + 1);
        //添加信息
        elementData[size++] = object;


/*        //老數組容量大小
        int oldCapacity = elementData.length;
        //新數組容量大小
        int newCapacity;
        //判斷數組實際容量是否 >  elementData容量
        if (size == oldCapacity) {

            //定義新數組容量大小
            newCapacity = 2 * size;
            //將老數組的數據寫入到新數組中
       Object[] newObjects = new Object[newCapacity];
           for (int i = 0; i < oldCapacity; i++) {
                newObjects[i] = elementData[i];
            }
            elementData = newObjects;
        }*/

    }

    /*
     *  方法:擴容
     *  參數:minCapacity 最小擴容容量
     * */
    public void ensureCapacityInternal(int minCapacity) {
        //如果當前存放數據的數組 爲一個空數組,那麼他擴容的大小爲:
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //最小擴容容量:數組默認大小值與參數比較
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //老數組容量大小
        int oldCapacity = elementData.length;
        //新數組容量大小
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果新數組容量大小 小於最小擴容容量
        if (newCapacity < minCapacity)
            newCapacity = minCapacity;//最少保證容量和 最小擴容容量 一樣
        //如果新數組容量大小 大於 最大容容量
        if (newCapacity > MAX_ARRAY_SIZE)
            newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;

        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    public Object get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
        return elementData[index];
    }

    /*
        String[] arr = {"A","B","C","D","E","F"};
        System.arraycopy(arr ,3,arr,2,2);
        從下標爲3的位置開始複製,複製的長度爲2(複製D、E),從下標爲2的位置開始替換爲D、E
        複製後的數組爲String[] arr = {"A","B","D","E","E","F"};

        刪除下標爲i=2的元素(c):
        從下標爲index+1=3的元素(D)copy ,

        從index=2的位置替換

        複製長度:numMoved=(Array.length-index-1=3)個,
    * */
    public Object remove(int index) {
        if (index > size || index < 0) {
            throw new IndexOutOfBoundsException("刪除越界。。");
        }
        int numMoved = size - index - 1;

        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);

        Object o = get(index);
        //將最後一個元素爲空
        elementData[--size] = null;
        return o;
    }

    //刪除對象
    public boolean remove(Object o) {
        for (int index = 0; index < size; index++) {
            if (o.equals(elementData[index])) {
                remove(index);
                return true;
            }
        }
        return false;

    }
}

Vector底層實現原理

Vector是線程安全的,但是性能比ArrayList要低。
ArrayList,Vector主要區別爲以下幾點:
(1):Vector是線程安全的,源碼中有很多的synchronized可以看出,而ArrayList不是。導致Vector效率無法和ArrayList相比;
(2):ArrayList和Vector都採用線性連續存儲空間,當存儲空間不足的時候,ArrayList默認增加爲原來的50%,Vector默認增加爲原來的一倍;
(3):Vector可以設置capacityIncrement,而ArrayList不可以,從字面理解就是capacity容量,Increment增加,容量增長的參數。

演示多線程下ArrayList不安全問題
案例:
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		final ArrayList<String> arrayList = new ArrayList<String>();
		for (int i = 0; i < 3; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					arrayList.add("aaaaa");
					System.out.println(arrayList);
				}
			}).start();
		}
	}
}
錯誤截圖:

在這裏插入圖片描述

導致原因:

併發爭搶修改導致,參考花名冊簽名情況。
一個人正在寫入,另外一個同學過來搶奪,導致數據不一致異常

解決方案:

方案一:
final List arrayList = new Vector();
使用vector類vector的add方法加鎖,使數據性一致,但併發性下降<
/font>

方案二:

final List<String> arrayList =
 Collections.synchronizedList(new ArrayList<String>());Collections工具類

方案三:

final List<String> arrayList =new CopyOnWriteArrayList<>();

CopyOnWrite容器即寫時複製的容器。往一個容器添加元素的時候,不直接往當前容器object[]添加,而是先將當前容器object[]進Copy,複製出一個新的容器object[] newELements,然後向新的容器object[] newELements 裏添加元素,添加完元素之後,再將原容器的引用指向新的容器setArray(newELements);.這樣做的好處是可以對CopyonWrite容器進行併發的讀,而不需要加鎖,因爲當前容器不會添加任何元素。所以CopyOnWrite 容器也是一. 種讀寫分離的思想,讀和寫不同的容器

CopyOnWriteArrayList的Add方法
   public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章