1. Vector简介
Vector是JDK1.0版本就推出的一个类,和ArrayList一样,继承自AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable接口,底层也是基于数组实现的,不同的是它是一个线程安全的类。
2. Vector实现
1. 核心属性
//底层存储数组 protected Object[] elementData; //数组内实际存储元素个数 protected int elementCount; //扩容容量 protected int capacityIncrement;
与ArrayList相比,核心属性多了一个扩容容量,可在初始化时指定,若不指定默认扩容2倍。
2. 构造函数
//initialCapacity:指定初始容量,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; } //只指定初始容量,每次扩容2倍 public Vector(int initialCapacity) { this(initialCapacity, 0); } //无参构造,默认初始容量10,每次扩容2倍 public Vector() { this(10); } //传入一个集合c,初始容量与c等于c的大小,每次扩容2倍 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); }
3. 存储元素
和ArrayList不同的是,Vector里每个存储元素的方法都是synchronized修饰的,所以Vector是线程安全的,但是由于锁的存在,效率会比较低。
当对Vector进行元素添加的时候,都会检查底层数组的容量是否足够,若是不够则进行自动扩容,每次对数组进行增删改的时候都会增加modCount(继承自AbstractList的属性,用来统计修改次数),添加单个元素时一般会扩容2倍(如果构造函数中指定了扩容容量则以指定容量进行扩容),添加集合时则要取要添加的集合大小与自动扩容的大小的最大值进行扩容。
存储元素分为三种类型:追加,插入,替换,其中第二种和第三种类型都会检查下标是否越界(根据size属性而不是数组的长度)
(1)追加:这种方式最常用,直接添加到数组中最后一个元素的后面。
//追加一个元素,返回布尔值,同步方法 synchronized作用于普通方法相当于synchroniced(this),锁的是当前对象 public synchronized boolean add(E e) { modCount++;
//容量小于 elementCount+1 时自动扩容 ensureCapacityHelper(elementCount + 1);
//将元素追加到数组末端的同时将elementCount+1 elementData[elementCount++] = e; return true; } //追加一个集合,返回布尔值,同步方法 public synchronized boolean addAll(Collection<? extends E> c) { modCount++;
//将追加的集合转为数组 Object[] a = c.toArray();
//取追加的元素个数 int numNew = a.length;
//容量小于 elementCount+numNew 时自动扩若 ensureCapacityHelper(elementCount + numNew);
//通过System.arraycopy将集合追加到当前对象底层数组末端,效率较低 System.arraycopy(a, 0, elementData, elementCount, numNew);
//将追加的数组个数加进elementCount elementCount += numNew; return numNew != 0; } //添加一个元素,无返回值 add(E e)是在List中定义的方法,并且有返回值,同步方法
//该方法在其子类Stack中被调用,感觉和add方法没啥区别?? public synchronized void addElement(E obj) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = obj; }
(2)插入:当调用下面这两个方法向数组中添加元素或集合时,会先查找索引位置,然后将元素添加到索引处,最后把添加前索引后面的元素追加到新元素的后面。
//在指定位置插入一个元素,虽然没有加synchronized,但方法内调用的方法是synchronized修饰的,不明白为何不把这两个方法合并 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); }
//容量不足 elementCount+1 时自动扩容 ensureCapacityHelper(elementCount + 1);
//将元素index下标位置及后面的元素全部向后移一位 System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
//在index下标位置插入元素 elementData[index] = obj; elementCount++; } //在指定位置插入集合,同步方法 public synchronized boolean addAll(int index, Collection<? extends E> c) { modCount++;
//检查下标是否越界,越界则抛出异常 if (index < 0 || index > elementCount) throw new ArrayIndexOutOfBoundsException(index); //将需要插入的集合c转为数组a Object[] a = c.toArray();
//将数组a的大小赋值给numNew int numNew = a.length;
//当容量小于 elementCount + numNew 时自动扩若 ensureCapacityHelper(elementCount + numNew); //获取插入位置index下标及其后面的元素个数 int numMoved = elementCount - index; if (numMoved > 0)
//将elementData从下标为index处开始的numMoved个元素复制到elementData的下标为 index+numNew 处及其后面 System.arraycopy(elementData, index, elementData, index + numNew, numMoved); //将数组a从下标为0开始的numNew个元素复制到elementData的下标为index处 System.arraycopy(a, 0, elementData, index, numNew); elementCount += numNew; return numNew != 0; }
(3)替换:调用该方法会将index位置的元素用新元素替代。
//将下标为index处替换成当前元素,同步方法 public synchronized E set(int index, E element) {
//检查下标是否越界,越界则抛出异常 if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); //取出index下标处的元素 E oldValue = elementData(index);
//将index下标处元素替换 elementData[index] = element;
//返回被替换的元素 return oldValue; } //直接替换,无返回值,同步方法 public synchronized void setElementAt(E obj, int index) {
//检查下标是否越界,越界则抛出异常 if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); }
//将index处元素直接替换 elementData[index] = obj; }
4. 元素读取
//获取指定下标位置的元素,同步方法 public synchronized E get(int index) { //检查下标是否越界,越界则抛出异常 if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); //取出底层存储数组index下标处的元素并返回 return elementData(index); }
5. 元素删除
(1) 按下标移除:
//重写 同步方法,返回被移除元素 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); //将最后一位置空的同时把elementCount - 1 elementData[--elementCount] = null; // Let gc do its work return oldValue; } //扩展 同步方法,无返回值 public synchronized void removeElementAt(int index) { modCount++; if (index >= elementCount) { throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount); } else if (index < 0) { //比remove方法多了index < 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 */ }
(2)移除指定元素
//重写方法 实则调用内部扩展的方法removeElement public boolean remove(Object o) { return removeElement(o); } //扩展 同步方法,返回布尔值 public synchronized boolean removeElement(Object obj) { modCount++; //取指定元素的下标 int i = indexOf(obj); if (i >= 0) { //将改下标的元素移除,复用removeElementAt的逻辑 removeElementAt(i); return true; } return false; }
(3)移除多个元素
//移除指定元素集合,复用父类的移除逻辑,重写的目的只是为了加锁 public synchronized boolean removeAll(Collection<?> c) { return super.removeAll(c); } //移除某个范围的所有元素,扩展 同步方法 protected synchronized void removeRange(int fromIndex, int toIndex) { modCount++; //获取被移除范围后面的元素个数 int numMoved = elementCount - toIndex; //把fromIndex后面的元素全部移到toIndex位置及其后面 System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved); // Let gc do its work int newElementCount = elementCount - (toIndex-fromIndex); //将大于等于新数组元素个数的下标元素全部置空 while (elementCount != newElementCount) elementData[--elementCount] = null; }
(4)移除所有元素
//扩展 同步方法,清空底层数组并将数量置0,无返回值 public synchronized void removeAllElements() { modCount++; // Let gc do its work for (int i = 0; i < elementCount; i++) elementData[i] = null; elementCount = 0; } //重写方法,为了加锁 public void clear() { removeAllElements(); }
(5)根据lambda表达式条件移除:removeIf
public synchronized boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); // figure out which elements are to be removed // any exception thrown from the filter predicate at this stage // will leave the collection unmodified 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(); } // shift surviving elements left over the spaces left by removed elements 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; }
//用法如下:把大于10的元素移除调用
Vector<Integer> vector = new Vector<Integer>(); vector.add(5); vector.add(6); vector.add(11); vector.add(12); vector.removeIf(Integer -> Integer > 10);
6. 调整数组容量
(1)扩容:手动扩容和自动扩容,手动扩容用于加入Vector的数据量较大时,提前扩容至需要的容量,避免添加时递增式反复调用System.arraycopy带来的性能损耗,自动扩容则由添加元素时内存不够时触发。
//对外开放的同步方法,提供手动扩容的功能,内部没有进行调用 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,则按照指定的扩容容量进行扩容,否则默认扩容2倍(这里是本方法和ArrayList的唯一区别之处) int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); //如果扩容后的容量小于新数组需要的最小容量,则扩容至新数组需要的最小容量 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //处理扩容后的容量大于预设的list最大值的情况 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { //大于int最大值,抛出内存溢出异常 if (minCapacity < 0) // overflow throw new OutOfMemoryError(); //新数组需要的最小容量大于预设的list最大值则扩容至int的最大值,否则扩容至预设的list最大值 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
(2)缩容:就是把底层数组的长度调整为元素实际个数大小,这个方法主要是为了防止Vector进行扩容时产生的空间浪费。
//同步方法 public synchronized void trimToSize() { modCount++; //获取底层数组的长度 int oldCapacity = elementData.length; //当元素个数小于数组长度时,通过System.arraycopy把空的元素全部去除 if (elementCount < oldCapacity) { elementData = Arrays.copyOf(elementData, elementCount); } }
7. 转为静态数组
//同步方法,返回Object类型数组 public synchronized Object[] toArray() { return Arrays.copyOf(elementData, elementCount); } //同步方法,返回指定类型和大小的数组,如:new Integer[2] public synchronized <T> T[] toArray(T[] a) { //如果传入数组a大小小于底层数组elementData元素个数,则直接复制 if (a.length < elementCount) return (T[]) Arrays.copyOf(elementData, elementCount, a.getClass()); //相反,则把数组中的元素全部复制到数组a中 System.arraycopy(elementData, 0, a, 0, elementCount); //这一步不清楚为何要这么做,如果a.length=3,elementCount=1,也只会把下标为1位置的元素置空,没管下标为2的位置,有点费解 if (a.length > elementCount) a[elementCount] = null; return a; }
8.实现了Cloneable接口,进行数据浅拷贝
public synchronized Object clone() { try { @SuppressWarnings("unchecked") Vector<E> v = (Vector<E>) super.clone(); v.elementData = Arrays.copyOf(elementData, elementCount); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }
9.实现Serializable 接口,启用其序列化功能
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { final java.io.ObjectOutputStream.PutField fields = s.putFields(); final Object[] data; synchronized (this) { fields.put("capacityIncrement", capacityIncrement); fields.put("elementCount", elementCount); data = elementData.clone(); } fields.put("elementData", data); s.writeFields(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gfields = in.readFields(); int count = gfields.get("elementCount", 0); Object[] data = (Object[])gfields.get("elementData", null); if (count < 0 || data == null || count > data.length) { throw new StreamCorruptedException("Inconsistent vector internals"); } elementCount = count; elementData = data.clone(); }
10. 实现了RandomAccess接口,启用随机访问
通过实现RandomAccess接口表明该类支持随机访问,可以采用for循环的方式进行遍历来提高性能。