ArrayList和CopyOnWriteArrayList

ArrayList源码

transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

默认大小空,什么时候开始有大小?
add方法

//这里看到add方法没有加任何锁,所以线程不安全的
 public boolean add(E e) {
 //size初始值0,这里开始+1,这里判断开始扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //将数据放到集合中
    elementData[size++] = e;
    return true;
}

//这里如果elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA都是空,默认10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    //源码默认是10大小
private static final int DEFAULT_CAPACITY = 10;

扩容

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //这里10+原来的一半,即为15,右移1位,相当于10除以2的一次幂,即为5(左移右移是最快的)
        //这里进行右移(>>)右移几位相当于除以2的几次幂;左移几位相当于乘以2的几次幂;
        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);
    }

线程不安全,add方法没有加锁,为了保证并发效率。
多线程调用add方法会报错 ConcurrentModificationException并发修改异常

所以ArrayList是线程不安全的。可以看到源码里面没有任何线程安全的代码。

ArrayList线程不安全,如何解决

  1. Vector线程安全,代码里加了synchonized关键字。但是Vector性能低,不用它
  2. Collections.synchronizedList 不用
    重点是下面这个
    CopyOnWriteArrayList集合

CopyOnWriteArrayList集合,JUC中的类。写时复制集合类。

写时复制,主要是读写分离的思想。

底层加了ReentrantLock锁。
不是直接往当前容器object【】添加,而是先将当前容器进行复制copy,得倒一个新的object[],然后向新的容器添加元素,添加完成后将原来容器引用指向新的容器。

这样好处是可以对容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素

//˙这里volatile关键字
private transient volatile Object[] array;
    
final Object[] getArray() {
    return array;
}
public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            //又看到了copyof方法,复制出一个新的容器,向新的容器添加元素
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            //将原来容器引用指向新的容器
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

结构–底层数组

可以看到,底层是数组,只不过这个ArrayList可可调整大小。而数组却是固定的大小,不能调整大小。说白了ArrayList集合就是可以调整大小的数组。(扩容)

特点–查询快,增删慢

数组有什么特点?
增删慢:每次删除元素,都需要更改数组长度、拷贝以及移动元素位置。
查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素。
所以ArrayList的特点就是查询快,增删慢。

ArrayList总结:

ArrayList是基于动态数组实现的,在增删时候,需要数组的拷⻉复制,copyof方法。

ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的⼀半,也就是变为原来的1.5倍

删除元素时不会减少容量,若希望减少容量则调⽤trimToSize()

它不是线程安全的。它能存放null值。
————————————————

ArrayList和LinkList区别?

ArrayList

基于动态数组的数据结构
对于随机访问的set和get,ArrayList要优于LinkedList
对于随机操作的add和remove,ArrayList不一定比LinkedList慢(ArrayList底层是由动态数组,因此并不是每次add和remove的时候都需要创建新数组)

LinkedList

基于链表的数据结构
对于顺序操作,LinkedList不一定比ArrayList慢
对于随机操作,LinkedList效率明显较低

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值