Java中的Vector詳細解讀
一、Vector介紹
Vector是實現(xiàn)了List接口的子類,其底層是一個對象數(shù)組,維護了一個elementData數(shù)組,是線程安全的,Vector類的方法帶有synchronized關(guān)鍵字,在開發(fā)中考慮線程安全中使用Vector。
二、源碼解析
1、Vector實現(xiàn)的接口
如下圖所示:
觀察上圖,發(fā)現(xiàn)Vector繼承了AbstractList<E>,并實現(xiàn)了三個接口,分別是:Serializable、Cloneable和RandomAccess。下面對Vector中繼承的類和實現(xiàn)的接口進行簡單的介紹:
- AbstractList類:該類實現(xiàn)了List接口里面的方法,并且為其提供了默認代碼實現(xiàn)。而List接口中主要定義了集合常用的方法讓ArrayList進行實現(xiàn),如:add、addAll、contains、remove、size、indexOf等方法。
- Serializable接口:主要用于序列化,即:能夠?qū)ο髮懭氪疟P。與之對應(yīng)的還有反序列化操作,就是將對象從磁盤中讀取出來。因此如果要進行序列化和反序列化,ArrayList的實例對象就必須實現(xiàn)這個接口,否則在實例化的時候程序會報錯(java.io.NotSerializableException)。
- Cloneable接口:實現(xiàn)Cloneable接口的類能夠調(diào)用clone方法,如果沒有實現(xiàn)Cloneable接口就調(diào)用方法,就會拋出異常(java.lang.CloneNotSupportedException)。
- RandomAccess接口:該接口表示可以隨機訪問ArrayList當中的數(shù)據(jù)。隨機訪問是指我們可以在常量時間復(fù)雜度內(nèi)進行數(shù)據(jù)的方法,因為ArrayList的底層實現(xiàn)是數(shù)組,而數(shù)組是可以隨機訪問的。
2、Vector的構(gòu)造方法
(1)無參構(gòu)造方法
public Vector() { this(10); }
總結(jié):Vector的無參構(gòu)造方法中會調(diào)用有參構(gòu)造方法,創(chuàng)建一個內(nèi)部數(shù)組,該內(nèi)部數(shù)組的初始容量為10,增量為0
(2)帶初始容量的構(gòu)造方法
public Vector(int initialCapacity) { this(initialCapacity, 0); }
總結(jié):構(gòu)造一個內(nèi)部數(shù)組,該數(shù)組的容量為指定的容量,增量為0。
(3)帶初始容量和增量的構(gòu)造方法
public Vector(int initialCapacity, int capacityIncrement) { super(); //如果初始容量小于0,拋出異常 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); //指定容量 this.elementData = new Object[initialCapacity]; //指定增量 this.capacityIncrement = capacityIncrement; }
總結(jié):構(gòu)造一個具有初始容量和增量的數(shù)組。
(4)集合型構(gòu)造方法
public Vector(Collection<? extends E> c) { elementData = c.toArray(); elementCount = elementData.length; // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, elementCount, Object[].class); }
總結(jié):構(gòu)造指定集合元素的數(shù)組。
3、Vector中的變量
Vector底層也是數(shù)組實現(xiàn)的,其主要變量有:
- elementData::Vector是基于數(shù)組的一個實現(xiàn),elementData就是底層的數(shù)組
- elementCount:數(shù)組元素個數(shù)
- capacityIncrement:指定Vector容量不足的擴容量,不指定的情況下默認翻倍
4、Vector主要方法解析
(1)add方法解析
從上面對于Vector構(gòu)造方法的分析,不難發(fā)現(xiàn)Vector和ArrayList的默認初始容量都是10。那么,我們看看Vector的add()方法又是如何實現(xiàn)的?
public synchronized boolean add(E e) { //AbstractList中的變量 modCount++; //確保數(shù)組容量是否足夠 ensureCapacityHelper(elementCount + 1); //把元素添加到數(shù)組中 elementData[elementCount++] = e; return true; }
可以看到:add方法添加一個元素到列表的末尾。它首先通過ensureCapacityHelper(elemetnCount+1)來保證Object[]數(shù)組有足夠的空間存放添加的數(shù)據(jù),然后再將添加的數(shù)據(jù)存放到數(shù)組對應(yīng)位置上。
private void ensureCapacityHelper(int minCapacity) { // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
通過ensureCapacityHelper()方法判斷最小容量和當前數(shù)組長度,若所需的最小容量大于數(shù)組大小,則需要進行擴容,然后調(diào)用grow()方法實現(xiàn)擴容。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { //舊數(shù)組的長度 int oldCapacity = elementData.length; //如果指定了增量,則新數(shù)組長度=舊數(shù)組長度+增量;如果沒有指定容量,則新數(shù)組長度=舊數(shù)組長度2倍 int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); //判斷新容量減去最小容量是否小于0,如果是第一次調(diào)用add,則必然小于 if (newCapacity - minCapacity < 0) //將最小容量賦給新容量 newCapacity = minCapacity; /判斷新容量減去最大數(shù)組大小是否大于0,如果時則計算出一個超大容量 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); //調(diào)用數(shù)組工具類方法,創(chuàng)建一個新數(shù)組,將新數(shù)組的地址賦值給elementData elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { //如果最小容量小于0,拋出異常;否則就比較并返回 if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
Step1:先將當前數(shù)組大小賦值給oldCapacity,然后判斷是否有指定增量的值,如果有,則新數(shù)組長度=舊數(shù)組長度+增量;如果沒有,則新數(shù)組長度=舊數(shù)組長度*2。
Step2:利用newCapacity進行兩次判斷:
第一次判斷 if (newCapacity - minCapacity < 0),判斷擴容后容量是否大于minCapacity,若小于minCapacity,則直接將minCapacity賦值給newCapacity第二次判斷 if (newCapacity - MAX_ARRAY_SIZE > 0),判斷newCapacity 是否超出了ArrayList所定義的最大容量,若超出了,則調(diào)用hugeCapacity()來比較minCapacity和 MAX_ARRAY_SIZE, 如果minCapacity大于MAX_ARRAY_SIZE,則新容量則為 Interger.MAX_VALUE,否則,新容量大小則為 MAX_ARRAY_SIZE。
Step3:最終得到newCapacity,然后調(diào)用Arrays.copyOf()方法進行擴容
綜合上述分析,有以下幾個點:
- Vector如果不指定初始容量,則默認創(chuàng)建一個長度為10的數(shù)組。
- Vector如果不指定初始增量,則擴容機制為:新數(shù)組長度=舊數(shù)組長度*2;如果指定初始增量,則擴容機制為:新數(shù)組長度=舊數(shù)組長度+增量。
- Vector的add()方法是加了synchronized關(guān)鍵字的,這就意味著它是線程安全的。
接下來可以看看如何在指定位置添加元素:
//在指定位置添加元素 public void add(int index, E element) { insertElementAt(element, index); } //添加元素到指定位置 public synchronized void insertElementAt(E obj, int index) { modCount++; //檢查位置合法性 if (index > elementCount) { throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); } //判斷是否需要擴容 ensureCapacityHelper(elementCount + 1); //拷貝數(shù)組 System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); //添加元素 elementData[index] = obj; elementCount++; }
可以看到:在指定位置添加元素,首先進行了數(shù)組范圍的檢查,防止越界,然后調(diào)用方法檢驗是否要擴容,且增量++,之后完成數(shù)組拷貝即可。
(2)remove()方法
public boolean remove(Object o) { return removeElement(o); } public synchronized boolean removeElement(Object obj) { modCount++; //獲取該元素所在的數(shù)組下標 int i = indexOf(obj); //如果該元素存在,則移除元素 if (i >= 0) { removeElementAt(i); return true; } return false; } public synchronized void removeElementAt(int index) { modCount++; //如果下標越界,拋出異常 if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); } else if (index < 0) { //下標小于0,拋出異常 throw new ArrayIndexOutOfBoundsException(index); } //刪除元素 int j = elementCount - index - 1; if (j > 0) { System.arraycopy(elementData, index + 1, elementData, index, j); } elementCount--; elementData[elementCount] = null; /* to let gc do its work */ }
由上述分析:刪除元素同樣需要進行范圍校驗。然后計算刪除需要移動的數(shù)據(jù),再通過數(shù)組拷貝移動數(shù)組。其次還有一個小細節(jié),可以發(fā)現(xiàn)remove()方法是有返回值的,而這個返回值就是我們刪除的元素的值。同樣的,真正移除元素的remove()方法也是加鎖了的。
(3)set()方法
該方法加了synchronized關(guān)鍵字保證安全性,用來設(shè)置指定下標的數(shù)據(jù),進行元素數(shù)據(jù)的更新。
public synchronized E set(int index, E element) { //判斷合法性 if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); //修改舊值 E oldValue = elementData(index); elementData[index] = element; return oldValue; }
(4) get()方法
該方法用來獲取對應(yīng)下標的數(shù)據(jù),也是加鎖的方法。
public synchronized E get(int index) { if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); return elementData(index); }
(5)其他方法
Vector中的其他方法,如:判斷是否為空、獲取長度等方法,都是加了鎖的。因此,可以認為Vector是線程安全的。
關(guān)于Vector中的迭代器,這里不再贅述,有興趣的可以看這篇文章,里面對Itr進行了介紹:深入理解ArrayList
三、總結(jié)
1、Vector簡單總結(jié)
- Vector的底層實現(xiàn)也是數(shù)組,在不指定初始容量的情況下,默認初始數(shù)組大小為10,其擴容機制為:當指定了增量的時候,新擴容的容量=舊數(shù)組長度+容量;如果沒有指定增量,新擴容容量=舊數(shù)組長度*2。
- Vector是線程安全的,因為它對很多方法都加鎖了。
- Vector和ArrayList都是數(shù)組實現(xiàn)的,因此其支持快速隨機訪問,但增加元素和刪除元素的操作卻是比較耗時的。
2、對比ArrayList
相同:兩個類都實現(xiàn)了List接口,它們都是有序且元素可重復(fù)的集合。
不同:
(1)ArrayList 是線程不安全的,Vector 是線程安全的。ArrayList 是線程不安全的,所以當我們不需要保證線程安全性的時候推薦使用 ArrayList,如果想要在多線程中使用 ArrayList 可以通過 Collections.synchronizedList(new ArrayList()) 或 new CopyOnWriteArrayList 的方式創(chuàng)建一個線程安全的 ArrayList 集合;Vector 類的所有方法都是同步的??梢杂袃蓚€線程安全的訪問一個 Vector 對象,但是一個線程訪問 Vector 的話會在同步操作上耗費大量的時間。
(2)ArrayList 使用默認構(gòu)造器創(chuàng)建對象時是在調(diào)用 add() 方法時對 ArrayList 的默認容量進行初始化的,Vector 在調(diào)用構(gòu)造器時就對容量進行了初始化
(3)ArrayList 存儲數(shù)據(jù)的 Object 數(shù)組使用了transient關(guān)鍵字,Vector 的 Object 數(shù)組沒有。
關(guān)于transient關(guān)鍵字的說明:如果用 transient 聲明一個實例變量,當對象存儲時,它的值不需要維持。這里的對象存儲是指,Java 的 serialization 提供的一種持久化對象實例的機制。當一個對象被序列化的時候,transient型變量的值不包括在序列化的表示中,然而非transient型的變量是被包括進去的。例如:當持久化對象時,可能有一個特殊的對象數(shù)據(jù)成員,我們不想用 serialization 機制來保存它。為了在一個特定對象的一個域上關(guān)閉 serialization,可以在這個域前加上關(guān)鍵字 transient。
簡單的說,就是被 transient 修飾的成員變量,在序列化的時候其值會被忽略,在被反序列化后, transient 變量的值被設(shè)為初始值, 如 int 型的是 0,對象型的是 null 。
(4)擴容機制不同。ArrayList擴容機制為變?yōu)樵瓉淼?.5倍,而Vector擴容時如果指定了增量,則新數(shù)組長度=舊數(shù)組長度+增量,如果沒有指定,就擴容為原來的2倍。
到此這篇關(guān)于Java中的Vector詳細解讀的文章就介紹到這了,更多相關(guān)Vector解讀內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項目如何使用多線程執(zhí)行定時任務(wù)
在SpringBoot項目中使用多線程執(zhí)行定時任務(wù),可以避免一個耗時任務(wù)影響其他任務(wù),通過配置線程池任務(wù)調(diào)度器,Spring會自動使用多線程執(zhí)行定時任務(wù)2025-01-01Spring Cloud Config RSA簡介及使用RSA加密配置文件的方法
Spring Cloud 為開發(fā)人員提供了一系列的工具來快速構(gòu)建分布式系統(tǒng)的通用模型 。本文重點給大家介紹Spring Cloud Config RSA簡介及使用RSA加密配置文件的方法,感興趣的朋友跟隨腳步之家小編一起學(xué)習吧2018-05-05java文件/圖片的上傳與下載以及MultipartFile詳解
文章介紹了MultipartFile類的使用,包括獲取文件名、文件類型、文件大小等方法,以及如何處理多文件上傳和文件大小限制,同時提供了文件上傳和下載的示例代碼2025-02-02Java基礎(chǔ)知識精通注釋與數(shù)據(jù)類型及常量與變量
本文給大家介紹了Java的注釋與數(shù)據(jù)類型和常量變量,這些都是最基礎(chǔ)的知識,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04