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();
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值