您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么使用CopyOnWriteArrayList”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么使用CopyOnWriteArrayList”吧!
由于本文的重點(diǎn)不是Vector
集合,因此只是簡(jiǎn)單的分析一下Vector
的初始化方法和添加元素的方法。
Vector
的底層實(shí)現(xiàn)和ArrayList
一樣,都是由數(shù)組實(shí)現(xiàn)的。
Vector
的主要變量如下:
/** * 存放元素的數(shù)組 */ protected Object[] elementData; /** * 元素個(gè)數(shù) */ protected int elementCount; /** * 擴(kuò)容自增容量大小 */ protected int capacityIncrement;
Vector
的初始化提供了三個(gè)方法,除了可以指定初始容量的大小,還可以指定擴(kuò)容容量的大小。構(gòu)造器分別如下:
無參構(gòu)造器
public Vector() { this(10); }
指定初始化容量的構(gòu)造器
public Vector(int initialCapacity) { this(initialCapacity, 0); }
指定初始化容量和擴(kuò)容容量大小的構(gòu)造器
public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; }
從上面的構(gòu)造器中可以看出,如果調(diào)用無參構(gòu)造器,則會(huì)創(chuàng)建一個(gè)初始化容量為10
,擴(kuò)容容量為0
的Vector
集合。
Vector
的擴(kuò)容機(jī)制和ArrayList
的很像,如果不清楚ArrayList
的擴(kuò)容機(jī)制,可以看看這篇文章。這里我們直接看Vector
的擴(kuò)容方法grow
。
private void grow(int minCapacity) { // overflow-conscious code // 初始化數(shù)組的長(zhǎng)度,默認(rèn)為10 int oldCapacity = elementData.length; // 是否指定擴(kuò)容容量,不指定擴(kuò)容為原來的2倍 int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
通過上面的方法,我們可以看出,如果指定了擴(kuò)容容量的大小則擴(kuò)容的新數(shù)組大小為原來的數(shù)組加上擴(kuò)容容量的大小,如果不指定擴(kuò)容容量的大小則擴(kuò)容的新數(shù)組大小為原來數(shù)組大小的2
倍。這樣擴(kuò)容為原來的2倍是很消耗空間的,這也是Vector
被棄用的原因之一。
除此之外,看過源碼的同學(xué)可能發(fā)現(xiàn)了,Vector
集合的所有操作元素的方法都加了synchronized
關(guān)鍵字,這就導(dǎo)致了操作Vector
的效率會(huì)非常低,在開發(fā)中,往往讀操作的使用頻率會(huì)遠(yuǎn)高于其他操作,而CopyOnWriteArrayList
就是這樣一種讀操作效率遠(yuǎn)高于寫操作效率的List,一起來看看。
CopyOnWriteArrayList
類圖:
CopyOnWrite
簡(jiǎn)稱COW,根據(jù)名字來看就是寫入時(shí)復(fù)制。意思就是大家共同去訪問一個(gè)資源,如果有人想要去修改這個(gè)資源的時(shí)候,就需要復(fù)制一個(gè)副本,去修改這個(gè)副本,而對(duì)于其他人來說訪問得資源還是原來的,不會(huì)發(fā)生變化。
CopyOnWriteArrayList
底層是也是有數(shù)組實(shí)現(xiàn)的。 本文我們只解讀添加元素和讀取元素的區(qū)別,刪除修改元素原理和添加元素差不多,操作時(shí)都需要進(jìn)行加鎖,而讀操作不會(huì)加鎖。
CopyOnWriteArrayList
主要有以下兩個(gè)變量:
// 獨(dú)占鎖 final transient ReentrantLock lock = new ReentrantLock(); // 存放元素的數(shù)組 private transient volatile Object[] array;
我們仔細(xì)來分析一下上面兩個(gè)屬性,這兩個(gè)思想是 CopyOnWriteArrayList
的核心 。
lock:ReentrantLock,獨(dú)占鎖,多線程運(yùn)行的情況下,只有一個(gè)線程會(huì)獲得這個(gè)鎖,只有釋放鎖后其他線程才能獲得。
array:存放數(shù)據(jù)的數(shù)組,關(guān)鍵是被volatile
修飾了,被volatile
修飾,就保證了可見性,也就是一個(gè)線程修改后,其他線程立即可見。
最常用的初始化方式如下:
/** * Creates an empty list. */ public CopyOnWriteArrayList() { setArray(new Object[0]); } /** * Sets the array. */ final void setArray(Object[] a) { array = a; }
初始化只是創(chuàng)建了一個(gè)空的數(shù)組,并將array
指向它。
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { // 獲取原來的數(shù)組 Object[] elements = getArray(); // 原來數(shù)組的長(zhǎng)度 int len = elements.length; // 創(chuàng)建一個(gè)長(zhǎng)度+1的新數(shù)組,并將原來數(shù)組的元素復(fù)制給新數(shù)組 Object[] newElements = Arrays.copyOf(elements, len + 1); // 元素放在新數(shù)組末尾 newElements[len] = e; // array指向新數(shù)組 setArray(newElements); return true; } finally { lock.unlock(); } }
添加數(shù)組的步驟如下:
獲得獨(dú)占鎖,將添加功能加鎖
獲取原來的數(shù)組,并得到其長(zhǎng)度
創(chuàng)建一個(gè)長(zhǎng)度為原來數(shù)組長(zhǎng)度+1的數(shù)組,并拷貝原來的元素給新數(shù)組
追加元素到新數(shù)組末尾
指向新數(shù)組
釋放鎖
這個(gè)過程是線程安全的,COW的核心思想就是每次修改的時(shí)候拷貝一個(gè)新的資源去修改,add()
方法再拷貝新資源的時(shí)候?qū)?shù)組容量+1,這樣雖然每次添加元素都會(huì)浪費(fèi)一定的空間,但是數(shù)組的長(zhǎng)度正好是元素的長(zhǎng)度,也在一定程度上節(jié)省了擴(kuò)容的開銷。
public E get(int index) { return get(getArray(), index); } final Object[] getArray() { return array; } private E get(Object[] a, int index) { return (E) a[index]; }
讀操作是天然安全的操作,而且數(shù)組本身會(huì)進(jìn)行檢查越界問題,因此獲取元素的方法很簡(jiǎn)單,只是根據(jù)索引獲取該元素。
public int size() { return getArray().length; }
由于CopyOnWriteArrayList
的底層數(shù)組長(zhǎng)度,本身就是元素大小,因此size()
方法只要返回?cái)?shù)組長(zhǎng)度就可以了。
到此,相信大家對(duì)“怎么使用CopyOnWriteArrayList”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。