Java 集合深入理解(12):古老的 Vector

拭心發表於2016-10-23

點選檢視 Java 集合框架深入理解 系列, - ( ゜- ゜)つロ 乾杯~


今天刮颱風,躲屋裡看看 Vector !

都說 Vector 是執行緒安全ArrayList,今天來根據原始碼看看是不是這麼相似。

什麼是 Vector

這裡寫圖片描述

Vector 和 ArrayList 一樣,都是繼承自 AbstractList。它是 Stack 的父類。英文的意思是 “向量”。

這裡寫圖片描述

Vector 成員變數

這裡寫圖片描述

1.底層也是個陣列

protected Object[] elementData;

2.陣列元素個數,為啥不就叫 size 呢?奇怪

protected int elementCount;

3.擴容時增長數量,允許使用者自己設定。如果這個值是 0 或者 負數,擴容時會擴大 2 倍,而不是 1.5

protected int capacityIncrement;

4.預設容量

private static final int DEFAULT_SIZE = 10;

Vector 的 4 種構造方法

//建立預設容量 10 的陣列,同時增長量為 0 
public Vector() {
    this(DEFAULT_SIZE, 0);
}

//建立一個使用者指定容量的陣列,同時增長量為 0 
public Vector(int capacity) {
    this(capacity, 0);
}

//建立指定容量大小的陣列,設定增長量。如果增長量為 非正數,擴容時會擴大兩倍
public Vector(int capacity, int capacityIncrement) {
    if (capacity < 0) {
        throw new IllegalArgumentException("capacity < 0: " + capacity);
    }
    elementData = newElementArray(capacity);
    elementCount = 0;
    this.capacityIncrement = capacityIncrement;
}

//建立一個包含指定集合的陣列
public Vector(Collection<? extends E> c) {
    //轉成陣列,賦值
    elementData = c.toArray();
    elementCount = elementData.length;

    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    //可能有這個神奇的 bug,用 Arrays.copyOf 重新建立、複製
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

一個內部方法,返回一個新陣列:

private E[] newElementArray(int size) {
    return (E[]) new Object[size];
}

Vector 的成員方法

1.先來看 JDK 7 中 Vector 的 3 種擴容方式:

//根據指定的容量進行擴容   
private void grow(int newCapacity) {
    //建立個指定容量的新陣列,這裡假設指定的容量比當前陣列元素個數多
    E[] newData = newElementArray(newCapacity);
    //把當前陣列複製到新建立的陣列
    System.arraycopy(elementData, 0, newData, 0, elementCount);
    //當前陣列指向新陣列
    elementData = newData;
}

//預設增長一倍的擴容
private void growByOne() {
    int adding = 0;
    //擴容量 capacityIncrement 不大於 0,就增長一倍
    if (capacityIncrement <= 0) {
        if ((adding = elementData.length) == 0) {
            adding = 1;
        }
    } else {
        //否則按擴容量走
        adding = capacityIncrement;
    }

    //建立個新陣列,大小為當前容量加上 adding
    E[] newData = newElementArray(elementData.length + adding);
    //複製,賦值
    System.arraycopy(elementData, 0, newData, 0, elementCount);
    elementData = newData;
}

//指定預設擴容數量的擴容
private void growBy(int required) {
    int adding = 0;
    //擴容量 capacityIncrement 不大於 0
    if (capacityIncrement <= 0) {
        //如果當前陣列內沒有元素,就按指定的數量擴容
        if ((adding = elementData.length) == 0) {
            adding = required;
        }
        //增加擴容數量到 指定的以上
        while (adding < required) {
            adding += adding;
        }
    } else {
        //擴容量大於 0 ,還是按指定的擴容數量走啊
        adding = (required / capacityIncrement) * capacityIncrement;
        //不過也可能出現偏差,因為是 int 做除法,所以擴容值至少是 指定擴容量的一倍以上
        if (adding < required) {
            adding += capacityIncrement;
        }
    }
    //建立,複製,賦值一條龍
    E[] newData = newElementArray(elementData.length + adding);
    System.arraycopy(elementData, 0, newData, 0, elementCount);
    elementData = newData;
}

2.(我能說一開始看錯了,看成 JDK7 的了嗎 - -)再來看JDK 8 中的擴容機制,變成一種了:

//擴容,傳入最小容量,跟 ArrayList.grow(int) 很相似,只是擴大量不同 
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    //如果增長量 capacityIncrement 不大於 0 ,就擴容 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);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

3.Vector中的 5 種新增元素的方法

//擴容前兆,檢查數量
private void ensureCapacityHelper(int minCapacity) {
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

//在指定位置插入一個元素,同步的
public synchronized void insertElementAt(E obj, int index) {
    modCount++;
    if (index > elementCount) {
        throw new ArrayIndexOutOfBoundsException(index
                                                 + " > " + elementCount);
    }
    ensureCapacityHelper(elementCount + 1);
    //擴容後就把插入點後面的元素統一後移一位
    System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
    //賦值
    elementData[index] = obj;
    elementCount++;
}

//尾部插入元素,同步的
public synchronized void addElement(E obj) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = obj;
}

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

//新增一個集合到尾部,同步的
public synchronized boolean addAll(Collection<? extends E> c) {
    modCount++;
    //轉成陣列
    Object[] a = c.toArray();
    int numNew = a.length;
    //擴容,複製到陣列後面
    ensureCapacityHelper(elementCount + numNew);
    System.arraycopy(a, 0, elementData, elementCount, numNew);
    elementCount += numNew;
    return numNew != 0;
}

//新增一個結合到指定位置,同步的
public synchronized boolean addAll(int index, Collection<? extends E> c) {
    modCount++;
    if (index < 0 || index > elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityHelper(elementCount + numNew);

    //要移動多少個元素
    int numMoved = elementCount - index;
    if (numMoved > 0)
        //把插入位置後面的元素後移這麼多位
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    //複製元素到陣列中
    System.arraycopy(a, 0, elementData, index, numNew);
    elementCount += numNew;
    return numNew != 0;
}

最後還有個 ListIterator 的新增方法

    public void add(E e) {
        int i = cursor;
        synchronized (Vector.this) {
            checkForComodification();
            Vector.this.add(i, e);
            expectedModCount = modCount;
        }
        cursor = i + 1;
        lastRet = -1;
    }

4.Vector 中的 9 種刪除方法

//刪除指定位置的元素,同步的
public synchronized void removeElementAt(int index) {
    modCount++;
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }
    int j = elementCount - index - 1;
    if (j > 0) {
        //把刪除位置後面的元素往前移一位
        System.arraycopy(elementData, index + 1, elementData, index, j);
    }
    elementCount--;
    //最後多餘的一位置為 null
    elementData[elementCount] = null; /* to let gc do its work */
}

//刪除指定元素,同步的
public synchronized boolean removeElement(Object obj) {
    modCount++;
    int i = indexOf(obj);
    if (i >= 0) {
        removeElementAt(i);
        return true;
    }
    return false;
}

E elementData(int index) {
    return (E) elementData[index];
}

//刪除指定位置的元素
public synchronized E remove(int index) {
    modCount++;
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    E oldValue = elementData(index);

    //找到刪除該元素後,後面有多少位元素需要前移一位
    int numMoved = elementCount - index - 1;
    if (numMoved > 0)
        //遷移一位
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //最後一位置為 null,不浪費空間
    elementData[--elementCount] = null; // Let gc do its work

    return oldValue;
}

public boolean remove(Object o) {
    return removeElement(o);
}

//刪除指定集合的所有元素,同步的
public synchronized boolean removeAll(Collection<?> c) {
    //直接呼叫 AbstractCollection 的 removeAll 方法,用迭代器挨個刪除
    return super.removeAll(c);
}

//刪除所有元素,同步的
public synchronized void removeAllElements() {
    modCount++;
    // 挨個置為空,Let gc do its work
    for (int i = 0; i < elementCount; i++)
        elementData[i] = null;

    elementCount = 0;
}

//刪除指定範圍的元素,同步的
protected synchronized void removeRange(int fromIndex, int toIndex) {
    modCount++;
    //把結束位置以後的元素向前移動 指定數量個位置,覆蓋
    int numMoved = elementCount - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
                     numMoved);

    // 把多餘的位置置為 null
    int newElementCount = elementCount - (toIndex-fromIndex);
    while (elementCount != newElementCount)
        elementData[--elementCount] = null;
}

//排除異己,同步的
public synchronized boolean retainAll(Collection<?> c) {
    return super.retainAll(c);
}

//JDK 1.8 新增的
public synchronized boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    // 將要刪除的內容加入 removeSet
    int removeCount = 0;
    final int size = elementCount;
    final BitSet removeSet = new BitSet(size);
    final int expectedModCount = modCount;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        @SuppressWarnings("unchecked")
        final E element = (E) elementData[i];
        if (filter.test(element)) {
            removeSet.set(i);
            removeCount++;
        }
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }

    // 遍歷,刪除
    final boolean anyToRemove = removeCount > 0;
    if (anyToRemove) {
        final int newSize = size - removeCount;
        for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
            i = removeSet.nextClearBit(i);
            elementData[j] = elementData[i];
        }
        for (int k=newSize; k < size; k++) {
            elementData[k] = null;  // Let gc do its work
        }
        elementCount = newSize;
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

    return anyToRemove;
}

寫“同步的”寫的手抽筋,還是統計不是同步的方法吧 - -。

5. Vector 中的修改方法

//修改指定位置為指定元素
public synchronized E set(int index, E element) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    //找到這個元素,直接設定新值
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

//修改指定位置為指定元素
public synchronized void setElementAt(E obj, int index) {
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    //陣列就是方便,直接更新就好了
    elementData[index] = obj;
}

//修改陣列容量
public synchronized void setSize(int newSize) {
    modCount++;
    //元素個數超出容量就要擴容
    if (newSize > elementCount) {
        ensureCapacityHelper(newSize);
    } else {
        //新增 elementCount - newSize 個元素
        for (int i = newSize ; i < elementCount ; i++) {
            elementData[i] = null;
        }
    }
    elementCount = newSize;
}

//排序,修改順序
public synchronized void sort(Comparator<? super E> c) {
    final int expectedModCount = modCount;
    //用的是 Arrays.sort 
    Arrays.sort((E[]) elementData, 0, elementCount, c);
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}

//縮小陣列容量,減少佔用資源
public synchronized void trimToSize() {
    modCount++;
    int oldCapacity = elementData.length;
    if (elementCount < oldCapacity) {
        //新建個小點的陣列,賦值
        elementData = Arrays.copyOf(elementData, elementCount);
    }
}

6. Vector 中的查詢

//查詢 o 從指定位置 index 開始第一次出現的位置
public synchronized int indexOf(Object o, int index) {
    if (o == null) {
        for (int i = index ; i < elementCount ; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = index ; i < elementCount ; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

//查詢 o 在陣列中首次出現的位置
public int indexOf(Object o) {
    return indexOf(o, 0);
}

//是否包含 O 
public boolean contains(Object o) {
    return indexOf(o, 0) >= 0;
}

//是否包含整個集合
public synchronized boolean containsAll(Collection<?> c) {
    //呼叫 AbstractCollection 的方法,使用迭代器挨個遍歷查詢,兩重迴圈
    return super.containsAll(c);
}

//第一個元素,其實提供了 get() 方法就夠了
public synchronized E firstElement() {
    if (elementCount == 0) {
        throw new NoSuchElementException();
    }
    return elementData(0);
}

//最後一個元素,其實提供了 get() 方法就夠了
public synchronized E lastElement() {
    if (elementCount == 0) {
        throw new NoSuchElementException();
    }
    return elementData(elementCount - 1);
}

public synchronized boolean isEmpty() {
    return elementCount == 0;
}

//實際包含元素個數
public synchronized int size() {
    return elementCount;
}

//陣列大小,>= 元素個數
public synchronized int capacity() {
    return elementData.length;
}

7. Vector 也可以轉成陣列

public synchronized Object[] toArray() {
    return Arrays.copyOf(elementData, elementCount);
}

//跟 ArrayList 簡直一樣
public synchronized <T> T[] toArray(T[] a) {
    if (a.length < elementCount)
        return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass());

    System.arraycopy(elementData, 0, a, 0, elementCount);

    if (a.length > elementCount)
        a[elementCount] = null;

    return a;
}

8. Vector 中的迭代器

普通迭代器 Iterator:

public synchronized Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        // 呼叫 next() 前的檢查
        return cursor != elementCount;
    }

    public E next() {
        //注意了,Vector 連迭代器的方法也加了同步
        synchronized (Vector.this) {
            checkForComodification();
            int i = cursor;
            if (i >= elementCount)
                throw new NoSuchElementException();
            cursor = i + 1;
            return elementData(lastRet = i);
        }
    }

    public void remove() {
        if (lastRet == -1)
            throw new IllegalStateException();
        //注意了,Vector 連迭代器的方法也加了同步
        synchronized (Vector.this) {
            checkForComodification();
            Vector.this.remove(lastRet);
            expectedModCount = modCount;
        }
        cursor = lastRet;
        lastRet = -1;
    }

    //大概看下這個 1.8 的方法
    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        synchronized (Vector.this) {
            final int size = elementCount;
            int i = cursor;
            if (i >= size) {
                return;
            }
    @SuppressWarnings("unchecked")
            final E[] elementData = (E[]) Vector.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                action.accept(elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

ListIterator:

public synchronized ListIterator<E> listIterator(int index) {
    if (index < 0 || index > elementCount)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}

final class ListItr extends Itr implements ListIterator<E> {
    ListItr(int index) {
        super();
        cursor = index;
    }

    public boolean hasPrevious() {
        return cursor != 0;
    }

    public int nextIndex() {
        return cursor;
    }

    public int previousIndex() {
        return cursor - 1;
    }

    public E previous() {
        synchronized (Vector.this) {
            checkForComodification();
            int i = cursor - 1;
            if (i < 0)
                throw new NoSuchElementException();
            cursor = i;
            return elementData(lastRet = i);
        }
    }

    public void set(E e) {
        if (lastRet == -1)
            throw new IllegalStateException();
        synchronized (Vector.this) {
            checkForComodification();
            Vector.this.set(lastRet, e);
        }
    }

    public void add(E e) {
        int i = cursor;
        synchronized (Vector.this) {
            checkForComodification();
            Vector.this.add(i, e);
            expectedModCount = modCount;
        }
        cursor = i + 1;
        lastRet = -1;
    }
}

//1.8 新增的略過。。。

//還多了個 sort 方法,自己傳入的集合需要實現比較器
@SuppressWarnings("unchecked")
@Override
public synchronized void sort(Comparator<? super E> c) {
    final int expectedModCount = modCount;
    Arrays.sort((E[]) elementData, 0, elementCount, c);
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}

Vector 還支援 Enumeration 迭代:

public Enumeration<E> elements() {
    return new Enumeration<E>() {
        int count = 0;

        public boolean hasMoreElements() {
            return count < elementCount;
        }

        public E nextElement() {
            synchronized (Vector.this) {
                if (count < elementCount) {
                    return elementData(count++);
                }
            }
            throw new NoSuchElementException("Vector Enumeration");
        }
    };
}

總結

Vector 特點

  • 底層由一個可以增長的陣列組成
  • Vector 通過 capacity (容量) 和 capacityIncrement (增長數量) 來儘量少的佔用空間
  • 擴容時預設擴大兩倍
  • 最好在插入大量元素前增加 vector 容量,那樣可以減少重新申請記憶體的次數
  • 通過 iterator 和 lastIterator 獲得的迭代器是 fail-fast 的
  • 通過 elements 獲得的老版迭代器 Enumeration 不是 fail-fast 的
  • 同步類,每個方法前都有同步鎖 synchronized
  • 在 JDK 2.0 以後,經過優化,Vector 也加入了 Java 集合框架大家族

Vector VS ArrayList

共同點:

  • 都是基於陣列
  • 都支援隨機訪問
  • 預設容量都是 10
  • 都有擴容機制

區別:

  • Vector 出生的比較早,JDK 1.0 就出生了,ArrayList JDK 1.2 才出來
  • Vector 比 ArrayList 多一種迭代器 Enumeration
  • Vector 是執行緒安全的,ArrayList 不是
  • Vector 預設擴容 2 倍,ArrayList 是 1.5

如果沒有執行緒安全的需求,一般推薦使用 ArrayList,而不是 Vector,因為每次都要獲取鎖,效率太低。

Thanks

https://docs.oracle.com/javase/8/docs/api/java/util/Vector.html

http://blog.csdn.net/u011518120/article/details/52192680

相關文章