Vector 类提供了实现可增长数组的功能,与ArrayList类似,会对比这两个进行讲解。
属性:
protected Object[] elementData;
存储数据对象,与ArrayList一样,底层存储都是数组对象
protected int elementCount;
vector数据元素个数,小于等于elementData,ArrayList用的是size
protected int capacityIncrement;
vector数组容量扩容是增长大小,默认为0,ArrayList没有该参数,每次按照0.5的进行扩展。
创建
VectorAPI提供了三种构造方法
//时间调用构造方法 public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); //实例化数组对象,对象容量初始的大小,默认为10,扩容增长capacityIncrement为0 this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; } public Vector(int initialCapacity) { this(initialCapacity, 0); } public Vector() { this(10); }
对于默认new Vector来说会创建一个默认存储容量为10的数组对象,扩容增长为0,元素个数elementCount初始化为0。
增加元素
两种方式,一为直接插入,二为插入到指定索引位置。
public synchronized boolean add(E e) { modCount++; //验证对象数组的容量,如果容量不够,先进行扩容 ensureCapacityHelper(elementCount + 1); //将elementCount索引位置的填充元素,elementCount自加1 elementData[elementCount++] = e; return true; } // private void ensureCapacityHelper(int minCapacity) { //原有的对象数组的容量 int oldCapacity = elementData.length; //插入的索引位置大于原有的读写数组的容量进行扩容 if (minCapacity > oldCapacity) { Object[] oldData = elementData; //如果扩容增量大于0,则新的容量为原有容量+扩容增量,小于0,则按照原有容量扩大一倍。 int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2); //如果新的容量还是小于索引,直接将索引赋值给新容量 if (newCapacity < minCapacity) { newCapacity = minCapacity; } //按照新容量进行扩容 elementData = Arrays.copyOf(elementData, newCapacity); } //将元素插入到指定索引 public void add(int index, E element) { insertElementAt(element, index); } public synchronized void insertElementAt(E obj, int index) { modCount++; //index大于当前数组元素个数量直接抛出异常 if (index > elementCount) { throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount); } //验证对象数组的容量,如果容量不够,先进行扩容 ensureCapacityHelper(elementCount + 1); //将对象数组的索引index到elementCount-1对应的数据进行后移 System.arraycopy(elementData, index, elementData, index + 1, elementCount - index); //将对象数组索引位置为index填充为插入值 elementData[index] = obj; //元素个数+1 elementCount++; }
(1):直接插入,对象数组容量够,直接插入到elementCount所对应的索引位置。对象数组容量不够,先进行数组扩容,扩容默认为原对象数组容量的1倍,再索引位置为elementCount进行填充。
(2):插入指定位置,对象数组容量够,进行数组拷贝,拷贝的个数为elementCount-index,对象数组容量不够,先进行扩容,再进行拷贝。
对于NULL值,和重复值也没有进行处理。
读取元素
public synchronized E get(int index) { //大于数组元素个数直接抛出异常 if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); //取对应索引的元素 return (E)elementData[index]; }
读取元素比较简单,直接获取对象数组对应索引的值。
删除
//按照索引删除元素 public synchronized E remove(int index) { modCount++; //index大于等于elementCount抛出异常 if (index >= elementCount) throw new ArrayIndexOutOfBoundsException(index); //保存当前索引的旧值 Object oldValue = elementData[index]; //移动元素个数 int numMoved = elementCount - index - 1; //移动元素大于0,将索引index+1到elementCount-1前移一位 if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); //将elementCount-1,并将索引位置为elementCount-1设置为null elementData[--elementCount] = null; // Let gc do its work return (E)oldValue; }
删除涉及到数组的前移,前移的数据范围为删除索引的值+1到size-1的元素。注意我们发现删除并未和写入时一样进行对象数组的大小的自动收缩【即存储容量未发生变化】。这个和ArrayList一致。
按照元素值进行删除
public boolean remove(Object o) { return removeElement(o); } public synchronized boolean removeElement(Object obj) { modCount++; //获取索引位置 int i = indexOf(obj); //索引位置大于1进行删除 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) { 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 */ }
按照元素值进行删除,会遍历对象数组,遍历元素个数为删除元素的索引index+1,且只会删除该元素值出现第一个。并且在删除时,对象数组会进行拷贝,拷贝元素个数为elementCount-index-1。
压缩对象数组存储容量
public synchronized void trimToSize() { modCount++; int oldCapacity = elementData.length; if (elementCount < oldCapacity) { elementData = Arrays.copyOf(elementData, elementCount); } }
已elementCount 进行压缩。
清空元素
public synchronized void removeAllElements() { modCount++; // Let gc do its work for (int i = 0; i < elementCount; i++) elementData[i] = null; elementCount = 0; }
清空主要进行两件事情:(1):将相关的数据元素设置为NULL值。(2):将elementCount 设置为0
可以发现Vector和ArrayList操作基本一致,并且相关的操作方式一致,当时还是有差异。
总结:
(1):Vector的实际存储对象为一个数组对象。没有容量大小,可以无限增加【默认扩展因子为1,否则为原来的容量+capacityIncrement】,只受JVM内存大小限制。
(2):Vector中可以包含重复的元素,并可以存储NULL值。
(3):Vector的相关方法都是线程安全的【与ArrayList存在差异,其他都一样】。
(4):Vector的索引访问和元素更新方面有着非常优秀的性能,因为不需要花费精力做范围检查,所以从Vector的末端添加元素,删除元素也有着非常优秀的性能,除非Vector的存储容量不足而需要扩展内部数组的长度【扩展的时候需要数组拷贝】。插入和删除数据需要一个数组的拷贝,需要拷贝的元素数量是Vector的元素长度(elementData-1)减去索引号,对插入来讲,插入元素到Vector的第一个元素位置的性能最差,插入到最后一个位子的性能最好,数组拷贝所需的时间会根据元素的数量增加而显著增加。
(5):Vector查找指定位置的数据时间为O(1),插入到指定位置的数据时间为O(elementData-index-1)。
(6):Vector插入大量的元素,提前指定Vector的大小,防止在插入过程Vector进行扩容。
(7):Vector按照元素值进行删除时,会遍历数组对象,并且会进行数组拷贝,时间为O(elementData+1)+O(elementData-index-1),可以计算总共花费O(elementData),遍历的对象+拷贝的对对象就等于元素个数。
(8):Vector删除时,不会调整原有的数组存储容量的大小即在数组存储容量扩容的后删除所用扩容数据后也不会调整整个数组容量大小,手工调用trimToSize进行调整。