淺談Vector

淺談Vector

在之前的文章中,我們已經說過線程不安全的ArrayList和LinkedList,今天我們來講講一個線程安全的列表容器,他就是Vector,他的底層和ArrayList一樣使用數組來實現,不過不同於ArrayList的線程不安全,Vector中的公開方法基本都是帶有synchronized關鍵字的,雖然有些方法是非同步的,但內部總是會調用同步的方法保證整個方法的線程安全,即使是subList方法返回的子列表也是通過Collections類的synchronizedList來保證返回的子列表也是線程安全的:

public synchronized List<E> subList(int fromIndex, int toIndex) {
    return Collections.synchronizedList(super.subList(fromIndex, toIndex), this);
}

下面我們來介紹Vector的構造方法,Vector為我們提供了四種構造方法,前三種,基本就是無參調用單參,並傳遞一個默認容量值,單參調用雙參,並傳遞一個默認容量值

Vector();

public Vector() {
    //調用單參的構造方法,並傳遞一個初始容量值10
    this(10);
}

Vector(int initialCapacity);

public Vector(int initialCapacity) {
    //調用雙參的構造方法,並傳遞一個默認增量值0,增量值為0,代表每次擴容,容量會直接翻倍
    this(initialCapacity, 0);
}

Vector(int initialCapacity, int capacityIncrement);

public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

第四種構造方法,則是根據傳入的集合對象來初始化矢量列表的元素

Vector(Collection<? extends E> c);

public Vector(Collection<? extends E> c) {
    elementData = c.toArray();
    elementCount = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    //如果通過toArray方法轉換得到的數組類型不是Object[],則進行二次轉化
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

在方法講解之前,我們先來看看Vector的擴容機制,Vector提供了一個公開方法ensureCapacity(int minCapacity),通過該方法你可以傳入一個列表需要的最小容量值(但最終生成的新容量可能並不是這個值),然後做一個簡單的校驗,進一步調用內部私有的ensureCapacityHelper(int minCapacity)方法進行進一步判斷,如果該值小於列表當前的列表容量(非列表元素總數),不會進入到擴容方法grow中,進入grow方法後就開始了具體的擴容流程,具體請看下面的源碼:

public synchronized void ensureCapacity(int minCapacity) {
    if (minCapacity > 0) {
        modCount++;
        ensureCapacityHelper(minCapacity);
    }
}
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //按內置規則擴容獲得新的容量值,如果增量變量為0,容量翻倍,否則增加增量變量指定的增量值
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
    //將新的容量值與傳入的最小容量作比較,如果最小容量值大於新的容量值,則將最小容量值作為新的容量值,否則使用內置擴容規則獲得的容量值
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //最後將容量值與數組最大容量值(最大整型值-8)做比較
    //數組作為一個對象,需要一定的內存存儲對象頭信息,對象頭信息最大佔用內存不可超過8位元組
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //使用新容量和原列表生成新的列表並將元素進行拷貝,內部最終調用System.arraycopy方法進行元素拷貝
    elementData = Arrays.copyOf(elementData, newCapacity);
}

講完Vector的擴容規則,下面正式進入Vector中方法的講解

add(E element); addElement(E element);

這個兩個方法都是往矢量列表尾部插入新的元素,都是同步方法,不同點是add的返回值為布爾類型,而addElement沒有返回值

add(int index, E element); insertElementAt(E element, int index);

這兩個方法可以說是完全一致,在add方法內只是進行了insertElementAt方法的調用,雖然add是非同步方法,但insertElementAt是一個同步方法

addAll([int index,] Collection<? extends E> c);

該方法用於往矢量列表中存入集合中的所有元素,該方法存在單參和雙參兩個重載方法,都是同步方法,單參方法直接往尾部插入,雙參方法可以在指定索引位置插入

remove(int index);

該方法用於刪除矢量列表中指定索引位置的值,並返回被刪除的元素,該方法是同步方法

remove(Object o); removeElement(Object o); removeElementAt(int index);

這三個方法都是用於刪除矢量列表中的元素,其中remove是非同步的,其餘兩個方法都是同步的,但在remove方法調用了同步的removeElement方法,在removeElement方法中先是通過indexOf方法查找待刪除元素的索引,找到則調用removeElementAt方法刪除指定索引位置的元素值

removeAll(Collection<?> c)

該方法用於刪除傳入集合中指定的所有元素,是一個同步方法

retainAll(Collection<?> c);

該方法用於保留傳入集合中指定的所有元素,即刪除指定集合之外的所有元素,該方法是一個同步方法

clear(); removeAllElements();

這兩個方法都是用於刪除矢量列表中的所元素,其中clear方法是非同步的,內部調用了同步的removeAllElements方法

removeRange(int fromIndex, int toIndex);

該方法用於移除矢量列表中指定索引範圍的所有元素,該方法是一個同步方法

removeIf(Predicate<? super E> filter);

該方法用於移除矢量列表中符合指定條件的元素,該方法是一個同步方法,下面使用匿名內部類和Lambda做一個簡單的使用演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
System.out.println(list);
//如果不會使用Lambda表達式,則需要new Predicate接口對象並實現內部的test方法
list.removeIf(x -> x < 3);
System.out.println(list);
//對應的運行結果
[1, 2, 3, 4, 5]
[3, 4, 5]

set(int index, E element); setElementAt(E element, int index);

這兩個方法都是用於替換矢量列表中指定索引位置的元素值,都是同步方法,不同點是set方法返回被替換的元素值,而setElementAt沒有返回值

replaceAll(UnaryOperator operator);

該方法用於對矢量列表中的所有元素進行指定鉤子函數的操作,並且會保存對元素所做的修改,該方法是一個同步方法,下面使用匿名內部類結合Lambda表達式做一個簡單的使用演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
System.out.println(list);
//或者使用UnaryOperator接口創建匿名內部類,並實現apply方法亦可
list.replaceAll(x -> x + 2);
System.out.println(list);
//對應的運行結果
[1, 2, 3, 4, 5]
[3, 4, 5, 6, 7]

forEach()

該方法用於對矢量列表中的所有元素進行指定鉤子函數的操作,但是不會保存對元素所做的修改,該方法是一個同步方法,下面做簡單演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
System.out.println(list);
//或者使用Consumer接口創建匿名內部類,並實現accept方法亦可
list.forEach(x -> System.out.println(x += 2));
System.out.println(list);
//對應的運行結果
[1, 2, 3, 4, 5]
3
4
5
6
7
[1, 2, 3, 4, 5]

get(int index); elementAt(int index);

這兩個方法是一樣的,用於返回矢量列表中指定位置的元素值,都是同步方法

elements();

該方法用於返回矢量列表的枚舉對象,該方法是一個同步方法,可以使用返回的枚舉對象進行簡單迭代遍歷,下面是簡單應用:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
Enumeration<Integer> elements = list.elements();
while (elements.hasMoreElements()){
    System.out.println(elements.nextElement());
}

firstElement(); lastElement();

這兩個方法分別用於返回矢量列表中的首元素和尾元素,都是同步方法

indexOf(Object o); lastIndexOf(Object o);

這兩個方法分別用於返回矢量列表中第一次出現和最後一次出現指定元素的索引值,都是同步方法

lastIndexOf(Object o, int index);

該方法在lastIndexOf(Object o)的基礎上添加了一個索引值,用於在指定索引值之前的位置查找最後一次出現指定元素的索引值,該方法是同步方法

iterator();

該方法用於返回矢量列表的普通迭代器對象,迭代器提供了hasNext,next,remove,forEachRemaining方法用於對元素進行操作,iterator方法是是一個同步方法

listIterator([int index]);

該方法用於返回矢量列表的增強迭代器對象,提供了普通迭代器的所有功能,此外還有hasPrevious,nextIndex,previousIndex,previous,set,add方法,而且還可以通過index參數指定起始迭代位置,listIterator方法是一個同步方法

spliterator();

該方法用於將矢量列錶轉化為可分割的數組對象,轉化後的對象可以多次調用trySplit進行分割(五五分成),適合多線程的方式對大型矢量列表進行操作,簡單演示拆分過程:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);
list.add(6);list.add(7);list.add(8);list.add(9);list.add(0);
Spliterator<Integer> part1 = list.spliterator();
Spliterator<Integer> part2 = part1.trySplit();
Spliterator<Integer> part3 = part1.trySplit();
Spliterator<Integer> part4 = part2.trySplit();
part1.forEachRemaining(x-> System.out.print(x+" "));
System.out.println();
part2.forEachRemaining(x-> System.out.print(x+" "));
System.out.println();
part3.forEachRemaining(x-> System.out.print(x+" "));
System.out.println();
part4.forEachRemaining(x-> System.out.print(x+" "));
//對應的操作結果
8 9 0 
3 4 5 
6 7 
1 2 

capacity();

該方法用於返回矢量列表的容量值(elementData.length),該方法是一個同步方法

clone();

該方法用於返回一個矢量列表的淺克隆對象,該方法是同步方法

contains(Object o);

該方法用於判斷矢量列表中是否含有指定元素,該方法是一個非同步方法,但內部調用同步的indexOf方法

containsAll(Collection<?> c);

該方法用於判斷矢量列表中是否包含傳入集合的所有元素,該方法是一個同步方法

copyInto(Object[] array);

該方法用於將矢量列表的全部元素值拷貝到傳入的數組中,該方法是一個同步方法

equals(Object o);

該方法用於判斷傳入元素是否與當前元素相等(值相等,不一定非要是地址相等),該方法是同步方法

isEmpty();

該方法用於判斷矢量列表中是否存在元素,主要根據elementCount屬性值來判斷,該方法是同步方法

setSize(int size);

該方法用於將矢量列表的元素總數控制在傳入的size之內,如果size大於當前列表元素總數(elementCount),則將elementCount-size範圍的null當做列表元素(從這裡可以看出矢量列表中的元素存在與否不能根據是否為null來判斷,而需要根據elementCount來判斷),如果傳入的size小於列表元素總數,則將size-elementCount的值都置為null,最後都將elementCount=size值,該方法是一個同步方法

size();

該方法用於返回矢量列表的元素總數(elementCount),該方法是一個同步方法

sort(Comparator<? super E> c);

該方法用於將矢量列表裡的所有元素按照鉤子函數的規則進行排序,該方法是一個同步方法,下面對該方法做簡單演示:

Vector<Integer> list = new Vector<>();
list.add(1);list.add(4);list.add(5);list.add(2);list.add(3);
list.add(8);list.add(9);list.add(0);list.add(6);list.add(7);
System.out.println(list);
//或者使用Comparator接口創建匿名內部類,並實現compare方法亦可
list.sort((o1,o2) -> o1 - o2);
System.out.println(list);

subList(int fromIndex, int toIndex);

該方法用於返回當前矢量列表指定範圍內的子列表對象,該方法是一個同步方法,要注意的是最終返回的列表對象依舊是整個矢量列表對象,不過是添加了偏移量屬性,所以對子列表的操作都會反映到父列表中

toArray(T[] a);

該方法用於獲取矢量列表的數組對象,並轉化為參數對應的數組類型,該方法是一個同步方法,以下為該方法的簡單使用:

//如果直接調用list.toArray()方法,等同於list.toArray(new Object[0]);
String[] strs = list.toArray(new String[0]);

trimToSize();

該方法用於將矢量列表的容量調整為列表元素總數,調整中會生成新的數組對象,該方法為同步方法
如果對你有幫助,點個贊,或者打個賞吧,嘿嘿
整理不易,請尊重博主的勞動成果