CopyOnWriteArrayList簡介

  1. CopyOnWriteArrayList,寫數(shù)組的拷貝回懦,支持高效率并發(fā)且是線程安全的,讀操作無鎖的ArrayList气笙。所有可變操作都是通過對底層數(shù)組進(jìn)行一次新的復(fù)制來實(shí)現(xiàn)。
  2. CopyOnWriteArrayList適合使用在讀操作遠(yuǎn)遠(yuǎn)大于寫操作的場景里怯晕,比如緩存潜圃。它不存在擴(kuò)容的概念,每次寫操作都要復(fù)制一個副本舟茶,在副本的基礎(chǔ)上修改后改變Array引用谭期。CopyOnWriteArrayList中寫操作需要大面積復(fù)制數(shù)組,所以性能肯定很差
  3. 在迭代器上進(jìn)行的元素更改操作(remove吧凉、set和add)不受支持隧出。這些方法將拋出UnsupportedOperationException。

定義

CopyOnWriteArrayList跟ArrayList一樣實(shí)現(xiàn)了List<E>, RandomAccess, Cloneable, Serializable接口客燕,但是沒有繼承AbstractList鸳劳。

初始化時(shí)候新建一個容量為0的數(shù)組余素。

add(E e)方法

public boolean add(E e) {
    //獲得鎖垦写,添加的時(shí)候首先進(jìn)行鎖定
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //獲取當(dāng)前數(shù)組
        Object[] elements = getArray();
        //獲取當(dāng)前數(shù)組的長度
        int len = elements.length;
        //這個是重點(diǎn)谢翎,創(chuàng)建新數(shù)組赤屋,容量為舊數(shù)組長度加1伶授,將舊數(shù)組拷貝到新數(shù)組中
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //要添加的數(shù)據(jù)添加到新數(shù)組的末尾
        newElements[len] = e;
        //將數(shù)組引用指向新數(shù)組涮因,完成了添加元素操作
        setArray(newElements);
        return true;
    } finally {
        //解鎖
        lock.unlock();
    }
}

從上面來說拒迅,每次添加一個新元素都會長度加1沥寥,然后復(fù)制整個舊數(shù)組颤练,由此可見對于寫多的操作既忆,效率肯定不會很好。所以CopyOnWriteArrayList適合讀多寫少的場景。

add(int index, E element)方法

public void add(int index, E element) {
    //同樣也是先加鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //獲取舊數(shù)組
        Object[] elements = getArray();
        //獲取舊數(shù)組長度
        int len = elements.length;
        //校驗(yàn)指定的index
        if (index > len || index < 0)
            throw new IndexOutOfBoundsException("Index: "+index+
                                                ", Size: "+len);
        Object[] newElements;
        int numMoved = len - index;
        if (numMoved == 0)//需要插入的位置正好等于數(shù)組長度患雇,數(shù)組長度加1跃脊,舊數(shù)據(jù)拷貝到新數(shù)組
            newElements = Arrays.copyOf(elements, len + 1);
        else {
            //新數(shù)組長度增加1
            newElements = new Object[len + 1];
            //分兩次拷貝,第一次拷貝舊數(shù)組0到index處的到新數(shù)組0到index苛吱,第二次拷貝舊數(shù)組index到最后的數(shù)組到新數(shù)組index+1到最后
            System.arraycopy(elements, 0, newElements, 0, index);
            System.arraycopy(elements, index, newElements, index + 1,
                             numMoved);
        }
        //index初插入數(shù)據(jù)
        newElements[index] = element;
        //新數(shù)組指向全局?jǐn)?shù)組
        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 {
        //獲取原來的數(shù)組
        Object[] elements = getArray();
        //index位置的元素
        E oldValue = get(elements, index);
        //新舊值不相等才進(jìn)行替換
        if (oldValue != element) {
                //原來的長度
            int len = elements.length;
            //拷貝一份到新數(shù)組
            Object[] newElements = Arrays.copyOf(elements, len);
            //替換元素
            newElements[index] = element;
            //新數(shù)組指向全局?jǐn)?shù)組
            setArray(newElements);
        } else {
            // Not quite a no-op; ensures volatile write semantics
            setArray(elements);
        }
        return oldValue;
    } finally {
        //解鎖
        lock.unlock();
    }
}

get(int index)方法

讀的時(shí)候不加鎖酪术,代碼如下:

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

remove()

remove方法不再過多介紹,看完add和set方法應(yīng)該就能理解翠储。

迭代

內(nèi)部類COWIterator 實(shí)現(xiàn)了ListIterator接口绘雁。迭代的時(shí)候不能進(jìn)行remove,add援所,set等方法庐舟,會拋異常。

迭代速度快住拭,迭代時(shí)是迭代的數(shù)組快照挪略。

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

源碼分析

jdk1.7.0_71

//鎖,保護(hù)所有存取器
transient final ReentrantLock lock = new ReentrantLock();
//保存數(shù)據(jù)的數(shù)組
private volatile transient Object[] array;
final Object[] getArray() {return array;}
final void setArray(Object[] a) {array = a;}

空構(gòu)造,初始化一個長度為0的數(shù)組

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

利用集合初始化一個CopyOnWriteArrayList

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

利用數(shù)組初始化一個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() 轉(zhuǎn)換成數(shù)組

public Object[] toArray(){}

toArray(T a[]) 轉(zhuǎn)換成指定類型的數(shù)組

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

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

public E get(int index){}

set(int index, E element) 指定位置設(shè)置元素

寫元素的時(shí)候,先獲得鎖,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) 刪除指定區(qū)間的元素

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

addIfAbsent(E e) 如果元素不存在就添加進(jìn)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) 如果不存在就添加進(jìn)去

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市废酷,隨后出現(xiàn)的幾起案子瘟檩,更是在濱河造成了極大的恐慌,老刑警劉巖澈蟆,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件墨辛,死亡現(xiàn)場離奇詭異,居然都是意外死亡趴俘,警方通過查閱死者的電腦和手機(jī)睹簇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寥闪,“玉大人太惠,你說我怎么就攤上這事∑1铮” “怎么了凿渊?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缚柳。 經(jīng)常有香客問我埃脏,道長,這世上最難降的妖魔是什么秋忙? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任彩掐,我火速辦了婚禮,結(jié)果婚禮上灰追,老公的妹妹穿的比我還像新娘堵幽。我一直安慰自己狗超,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布朴下。 她就那樣靜靜地躺著努咐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桐猬。 梳的紋絲不亂的頭發(fā)上麦撵,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天刽肠,我揣著相機(jī)與錄音溃肪,去河邊找鬼。 笑死音五,一個胖子當(dāng)著我的面吹牛惫撰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播躺涝,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼厨钻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坚嗜?” 一聲冷哼從身側(cè)響起夯膀,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苍蔬,沒想到半個月后诱建,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碟绑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年俺猿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片格仲。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡押袍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凯肋,到底是詐尸還是另有隱情谊惭,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布侮东,位于F島的核電站圈盔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏苗桂。R本人自食惡果不足惜药磺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望煤伟。 院中可真熱鬧癌佩,春花似錦木缝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姚建,卻和暖如春矫俺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掸冤。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工厘托, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稿湿。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓铅匹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饺藤。 傳聞我的和親對象是個殘疾皇子包斑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容