CopyOnWriteArrayList源码分析

CopyOnWriteArrayList

        首先,我们先大致了解一下什么是CopyOnWriteArrayList。

        CopyOnWriteArrayList是一个线程安全的集合。当我们往一个集合容器中写入元素时(添加、修改、删除),并不会像之前的线程安全的方法一样,直接在方法上加锁,直接在集合容器中写入,而是先将当前集合容器进行Copy,复制出一个新的容器,然后在新的容器里写入元素,写入操作完成之后,再将原容器的引用指向新的容器。实现对CopyOnWrite集合容器写入操作时的线程安全,但同时并不影响进行并发的读取操作。

        CopyOnWriteArrayList相当于线程安全的ArrayList,内部存储结构采用Object[]数组,线程安全使用ReentrantLock实现,允许多个线程并发读取,但只能有一个线程写入。

源码分析

// 创建ReentrantLock锁
final transient ReentrantLock lock = new ReentrantLock();

private transient volatile Object[] array;

// 返回Object类型的数组
final Object[] getArray() {
    return array;
}

// 使当前数组重新指向
final void setArray(Object[] a) {
    array = a;
}

构造方法

// 无参构造方法,创建一个空的Object数组
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}
// 有参构造方法,传入一个集合,直接将集合内容复制到自己的数组
public CopyOnWriteArrayList(Collection<? extends E> c) {
    // 创建一个新Object数组
    Object[] elements;
    // 判断传入集合数组是否是CopyOnWriteArrayList类
    if (c.getClass() == CopyOnWriteArrayList.class)
        // 如果是,直接强制转换类型,让新创建的数组指向该数组
        elements = ((CopyOnWriteArrayList<?>)c).getArray();
    else {
        // 如果不是,将传入的集合转换为数组
        elements = c.toArray();
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // 判断当前对象的class是否是Object
        if (elements.getClass() != Object[].class)
            // 复制当前数组
            elements = Arrays.copyOf(elements, elements.length, Object[].class);
    }
    // 更新该数组集合
    setArray(elements); 
}

set(int index, E element):按照指定下标修改值

public E set(int index, E element) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
    	// 获得原数组
        Object[] elements = getArray();
        
        // 获取要被修改的值
        E oldValue = get(elements, index);

        // 判断修改的值和原来的值是否一样
        if (oldValue != element) {
        	// 如果不一样
        	// 获取要被修改的值的下标
            int len = elements.length;
            // 创建一个新的数组
            Object[] newElements = Arrays.copyOf(elements, len);
            // 将新的值存入原值的位置
            newElements[index] = element;
            setArray(newElements);
        } else {
            // Not quite a no-op; ensures volatile write semantics
            // 如果一样,直接返回原数组
            setArray(elements);
        }
        return oldValue;
    } finally {
        // 解锁
        lock.unlock();
    }
}

add():添加新元素

// 添加新元素
public boolean add(E e) {
	// 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
    	// 获取原数组
        Object[] elements = getArray();
        // 获取原数组的长度
        int len = elements.length;
        // 创建一个新的数组,数组长度为原数组+1
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        // 将要添加的值存入数组末尾
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
    	// 解锁
        lock.unlock();
    }
}

add(int index,E element):按照指定下标添加新元素

// 按照指定下标添加新元素
public void add(int index, E element) {
	// 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
    	// 获取原数组
        Object[] elements = getArray();
        // 获取原数组的长度
        int len = elements.length;
        // 判断要加入的位置是否小于0或者是否大于数组长度
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + len);
        Object[] newElements;
        // 计算新元素加入的下标位置
        int numMoved = len - index;
        // 判断是否加到数组末尾
        if (numMoved == 0)
        	// 创建一个新的数组,数组长度为原数组+1,并且直接复制
            newElements = Arrays.copyOf(elements, len + 1);
        else {
        	// 创建一个新的数组,数组长度为原数组+1
            newElements = new Object[len + 1];
            // 将新元素之前的元素复制到新数组
            System.arraycopy(elements, 0, newElements, 0, index);
            // 将新元素之后的元素复制到新数组
            System.arraycopy(elements, index, newElements, index + 1, numMoved);
        }
        newElements[index] = element;
        setArray(newElements);
    } finally {
    	// 解锁
        lock.unlock();
    }
}

remove(int index):按照指定下标删除元素

public E remove(int index) {
	// 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
    	// 获取原数组
        Object[] elements = getArray();
        // 获取原数组的长度
        int len = elements.length;
        // 获取要删除的元素值
        E oldValue = get(elements, index);
        // 计算要删除的元素下标
        int numMoved = len - index - 1;
        // 判断要删除的元素是否为尾元素
        if (numMoved == 0)
        	// 将尾元素之前的元素复制到新数组里,并返回
            setArray(Arrays.copyOf(elements, len - 1));
        else {
        	// 创建一个新数组,长度为原数组长度-1
            Object[] newElements = new Object[len - 1];
            // 复制删除元素之前的元素至新数组里
            System.arraycopy(elements, 0, newElements, 0, index);
            // 复制删除元素之后的元素至新数组里
            System.arraycopy(elements, index + 1, newElements, index, numMoved);
            setArray(newElements);
        }
        return oldValue;
    } finally {
    	// 解锁
        lock.unlock();
    }
}

removeRange(int fromIndex, int toIndex):删除指定下标区间的元素

void removeRange(int fromIndex, int toIndex) {
	// 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
    	// 获取原数组
        Object[] elements = getArray();
        // 获取原数组的长度
        int len = elements.length;

        // 判断删除开始的位置和结束的位置是否超过了数组长度
        if (fromIndex < 0 || toIndex > len || toIndex < fromIndex)
            throw new IndexOutOfBoundsException();
        // 计算删除后新数组的长度
        int newlen = len - (toIndex - fromIndex);
        // 计算删除元素的数量
        int numMoved = len - toIndex;
        // 判断要删除的元素数量是否为0
        if (numMoved == 0)
            setArray(Arrays.copyOf(elements, newlen));
        else {
        	// 创建一个新数组
            Object[] newElements = new Object[newlen];
            // 复制删除元素之前的元素至新数组里
            System.arraycopy(elements, 0, newElements, 0, fromIndex);
            // 复制删除元素之后的元素至新数组里
            System.arraycopy(elements, toIndex, newElements, fromIndex, numMoved);
            setArray(newElements);
        }
    } finally {
    	// 解锁
        lock.unlock();
    }
}

retainAll(Collection<?> c):判断集合间是否有交集并把原集合更新成交集

//判断集合间是否有交集并把原集合更新成交集
public boolean retainAll(Collection<?> c) {
	// 判断如果集合为空,抛出NullPointerException()异常
    if (c == null) throw new NullPointerException();
	// 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
		// 获取原数组
        Object[] elements = getArray();
		// 获取原数组的长度
        int len = elements.length;
		// 判断长度是否为0
        if (len != 0) {
            // temp array holds those elements we know we want to keep
            int newlen = 0;
			// 创建新数组
            Object[] temp = new Object[len];
			// 遍历原数组
            for (int i = 0; i < len; ++i) {
                Object element = elements[i];
				// 判断传入集合是否有原数组中元素
                if (c.contains(element))
                    temp[newlen++] = element;
            }
			// 设置原集合为交集
            if (newlen != len) {
                setArray(Arrays.copyOf(temp, newlen));
                return true;
            }
        }
        return false;
    } finally {
		// 解锁
        lock.unlock();
    }
}

clear():清空集合

// 清空集合
public void clear() {
	// 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
		// 设置数组为空数组
        setArray(new Object[0]);
    } finally {
		// 解锁
        lock.unlock();
    }
}

addAll(int index, Collection<? extends E> c):在原集合指定位置添加新集合

// 在原集合指定位置添加新集合
public boolean addAll(int index, Collection<? extends E> c) {
	// 指定集合转化成数组
    Object[] cs = c.toArray();
	// 加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
		// 获取原数组
        Object[] elements = getArray();
        // 获取原数组的长度
		int len = elements.length;
		// 判断指定下标是否越界
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + len);
        // 判断新集合是否为空
		if (cs.length == 0)
            return false;
        int numMoved = len - index;
        Object[] newElements;
		// 判断指定下标是否是尾下标
        if (numMoved == 0)
			// 复制新数组并且长度是原数组长度+新数组长度
            newElements = Arrays.copyOf(elements, len + cs.length);
        else {
            newElements = new Object[len + cs.length];
			// 从0开始复制index个元素
			System.arraycopy(elements, 0, newElements, 0, index);
			// 从原集合从index下标开始,结果集合从index+新数组长度开始复制numMoved个元素
            System.arraycopy(elements, index, newElements, index + cs.length, numMoved);
        }
		// 新数组从0开始,结果数组从index开始,复制新数组长度
        System.arraycopy(cs, 0, newElements, index, cs.length);
		// 设置数组为结果数组
        setArray(newElements);
        return true;
    } finally {
		// 解锁
        lock.unlock();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值