CopyOnWriteArrayList简介

  1. CopyOnWriteArrayList,写数组的拷贝,支持高效率并发且是线程安全的,读操作无锁的ArrayList。所有可变操作都是通过对底层数组进行一次新的复制来实现。
  2. CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。它不存在扩容的概念,每次写操作都要复制一个副本,在副本的基础上修改后改变Array引用。CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差
  3. 在迭代器上进行的元素更改操作(remove、set和add)不受支持。这些方法将抛出UnsupportedOperationException。

定义

CopyOnWriteArrayList跟ArrayList一样实现了List<E>, RandomAccess, Cloneable, Serializable接口,但是没有继承AbstractList。

初始化时候新建一个容量为0的数组。

add(E e)方法

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();
    }
}

从上面来说,每次添加一个新元素都会长度加1,然后复制整个旧数组,由此可见对于写多的操作,效率肯定不会很好。所以CopyOnWriteArrayList适合读多写少的场景。

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;
        //校验指定的index
        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];
            //分两次拷贝,第一次拷贝旧数组0到index处的到新数组0到index,第二次拷贝旧数组index到最后的数组到新数组index+1到最后
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index, newElements, index + 1,
                             numMoved);
        }
        //index初插入数据
        newElements[index] = element;
        //新数组指向全局数组
        setArray(newElements);
    } finally {
        //解锁
        lock.unlock();
    }
}

set(int index, E element)方法

public E set(int index, E element) {
    //修改元素之前首先加锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //获取原来的数组
        Object[] elements = getArray();
        //index位置的元素
        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();
    }
}

get(int index)方法

读的时候不加锁,代码如下:

public E get(int index) {
    return get(getArray(), index);
}
private E get(Object[] a, int index) {
    return (E) a[index];
}

remove()

remove方法不再过多介绍,看完add和set方法应该就能理解。

迭代

内部类COWIterator 实现了ListIterator接口。迭代的时候不能进行remove,add,set等方法,会抛异常。

迭代速度快,迭代时是迭代的数组快照。

/** Snapshot of the array */
private final Object[] snapshot;

源码分析

jdk1.7.0_71

//锁,保护所有存取器
transient final ReentrantLock lock = new ReentrantLock();
//保存数据的数组
private volatile transient Object[] array;
final Object[] getArray() {return array;}
final void setArray(Object[] a) {array = a;}

空构造,初始化一个长度为0的数组

public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }

利用集合初始化一个CopyOnWriteArrayList

public CopyOnWriteArrayList(Collection<? extends E> c) {}

利用数组初始化一个CopyOnWriteArrayList

public CopyOnWriteArrayList(E[] toCopyIn) {}

size() 大小

public int size() {}

isEmpty()是否为空

public boolean isEmpty(){}

indexOf(Object o, Object[] elements,int index, int fence) 元素索引

private static int indexOf(Object o, Object[] elements,int index, int fence) {}

indexOf() 元素索引

public int indexOf(Object o){}

indexOf(E e, int index) 元素索引

public int indexOf(E e, int index) {}

lastIndexOf(Object o, Object[] elements, int index) 元素索引,最后一个

private static int lastIndexOf(Object o, Object[] elements, int index) {}

lastIndexOf(Object o) 元素索引,最后一个

public int indexOf(E e, int index) {}

lastIndexOf(E e, int index) 元素索引,最后一个

public int lastIndexOf(E e, int index) {}

contains(Object o) 是否包含元素

public boolean contains(Object o){}

clone() 浅拷贝

public Object clone() {}

toArray() 转换成数组

public Object[] toArray(){}

toArray(T a[]) 转换成指定类型的数组

public <T> T[] toArray(T a[]) {}

E get(int index)获取指定位置的元素

public E get(int index){}

set(int index, E element) 指定位置设置元素

写元素的时候,先获得锁,finall块中释放锁

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(E e) 元素添加到末尾

public boolean add(E e) {}

add(int index, E element) 指定位置之后插入元素

public void add(int index, E element){}

remove(int index)删除指定位置的元素

public E remove(int index) {}

remove(Object o) 删除第一个匹配的元素

public boolean remove(Object o) {}

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

private void removeRange(int fromIndex, int toIndex) {}

addIfAbsent(E e) 如果元素不存在就添加进list中

public boolean addIfAbsent(E e){}

containsAll(Collection<?> c)是否包含全部

public boolean containsAll(Collection<?> c){}

removeAll(Collection<?> c) 移除全部包含在集合中的元素

public boolean removeAll(Collection<?> c){}

retainAll(Collection<?> c) 保留指定集合的元素,其他的删除

public boolean retainAll(Collection<?> c){}

addAllAbsent(Collection<? extends E> c) 如果不存在就添加进去

public int addAllAbsent(Collection<? extends E> c) {}

clear() 清空list

public void clear(){}

addAll(Collection<? extends E> c)添加集合中的元素到尾部

public void addAll(Collection<? extends E> c){}

addAll(int index, Collection<? extends E> c) 添加集合中元素到指定位置之后

public boolean addAll(int index, Collection<? extends E> c){}

toString()

public String toString(){}

equals(Object o)

public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;

        List<?> list = (List<?>)(o);
        Iterator<?> it = list.iterator();
        Object[] elements = getArray();
        int len = elements.length;
        for (int i = 0; i < len; ++i)
            if (!it.hasNext() || !eq(elements[i], it.next()))
                return false;
        if (it.hasNext())
            return false;
        return true;
    }

hashCode()

public int hashCode{}

listIterator(final int index)和 listIterator() 返回一个迭代器,支持向前和向后遍历

public ListIterator<E> listIterator(final int index) {}

public ListIterator<E> listIterator() {}

iterator() 只能向后遍历

public Iterator<E> iterator() {}

subList() 返回部分list

public List<E> subList(int fromIndex, int toIndex) {
    ...
    return new COWSubList<E>(this, fromIndex, toIndex);
    ...
}

参考

http://www.cnblogs.com/sunwei2012/archive/2010/10/08/1845656.html

http://my.oschina.net/jielucky/blog/167198

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容

  • java笔记第一天 == 和 equals ==比较的比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量...
    jmychou阅读 1,477评论 0 3
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,245评论 0 16
  • Collection ├List │├LinkedList │├ArrayList │└Vector │└Stac...
    AndyZX阅读 861评论 0 1
  • 璀璨的阳光照得我双眼发疼,我看不清这个世界也看不到自己的样子,我闭上了眼睛,我站在草场,努力的回想曾经的模样 。我...
    流浪的狐狸阅读 146评论 0 0
  • 距离我上一次给军迷专题投稿已经有一月余了吧,最近一直都没有什么灵感,也很紧张的在应付着考试。忙忙碌碌的做了很多事。...
    阳妹儿阅读 378评论 9 4