美文网首页IT技术篇
Java容器类源码-Vector的最全的源码分析

Java容器类源码-Vector的最全的源码分析

作者: 游戏原画设计 | 来源:发表于2019-01-09 13:19 被阅读0次

一、概述

我们都知道,在Java的Collections包含了List和Set,而List里面有ArrayList、LinkedList、还有Vector,对于很多Java初学者来说,前面两个比较常用,ArrayList查询效率比较高(底层是数组实现),而LinkedList的增删效率比较高(底层是双向链表实现)。那么Vector是什么呢?它和ArrayList、LinkedList一样,支持有序可重复地存放单列数据。它底层实现和ArrayList类似,都是数组实现,因此在技术面试中, 面试官比较喜欢拿ArrayList和Vector进行比较。尽管Vector在Java1.2之后进行了优化更新,但是Java官方还是推荐在不需要考虑线程安全的情况下,优先使用ArrayList。

二、Vector和ArrayList的对比

那么,Vector和ArrayList既然都是数组实现,它们到底有什么区别呢?通过对比它们的源码,可以总结出以下两个区别:

(1) Vector的所有方法都是有synchronized关键字的,即每一个方法都是同步的,所以在使用起来效率会非常低,但是保证了线程安全;而ArrayList的全部方法都是非同步的,所以相对Vector的效率会更高,所以它是线程不安全的。

(2) ArrayList在每次扩容时都是增加当前容量的1.5倍,而Vector在扩容时都是增加当前容量的两倍。

综上,Vector在增删改查等API上的实现都是和ArrayList类似甚至是相同的,所以如果你看了ArrayList的源码,那么Vector的源码你肯定是一下就看懂了,如果你还没看ArrayList的源码,请移步到《Java容器类源码-ArrayList的最全的源码分析》。所以在不需要考虑线程安全时,Java官方推荐我们使用ArrayList,那么,如果线程不安全时呢?我们应该怎么选择?我们都知道,Java在Collections类中给我们提供了同步ArrayList的方法public static <T> List<T> synchronizedList(List<T> list)。它可以帮助我们实现同步ArrayList,但是你通过看synchronizedList的实现就会知道,它其实是创建了一个新的类叫做SynchronizedList,它其实只是对ArrayList的增删改查等常用方法加了synchronized字段,所以它的效率其实和Vector是一样的,这个也在我们后面讲到的subList()源码里面得到印证,不信,我们在代码里面测试一下:

/**

* 测试使用Collections.synchronizedList调用增、删、改三个API操作50000个数据,求100次的平均时间

*

*/publicclassQTest{publicstaticvoidmain(String[] args){longtotalTime =0;for(inti =0; i <100; i++) {totalTime +=newQTest().getTime();}System.out.println("Collections.synchronizedList() 平均耗时:"+ (totalTime /100)); }publiclonggetTime(){Listlist= Collections.synchronizedList(newArrayList());longstartTime = System.currentTimeMillis();for(inti =0; i <50000; i++) {list.add(i *10);// 增加}for(inti =0; i <50000; i++) {// 修改list.set(i, i);}for(inti =0; i

* 测试使用Vector调用增、删、改三个API操作50000个数据,求100次的平均时间

*

*/publicclassPTest{publicstaticvoidmain(String[] args){longtotalTime =0;for(inti =0; i <100; i++) {totalTime +=newQTest().getTime();} System.out.println("vector 平均耗时:"+ (totalTime /100));}

publiclonggetTime(){Vectorvector=newVector<>();longnStartTime = System.currentTimeMillis();for(inti =0; i <100000; i++) {vector.add(i *10);// 增加}for(inti =0; i <100000; i++) {// 修改vector.set(i, i);}for(inti =0; i

这两段代码其实就是对同步的ArrayList进行增、删、改50000条数据,并重复100次,求平均时间。通过运行,在我的电脑(每台电脑的运行速度不一样)里Collections.synchronizedList的100次平均消耗时间是:129ms,而Vector的100次平均消耗时间是:130ms。在需要考虑线程安全时,你是用Vector和Collections.synchronizedList初始化的ArrayList大概效率是差不多的。所以,Vector也并非毫无用武之地,那么这次我们就一起探究一下Vector的源码。

三、源码解读

1. 继承、实现

extends:AbstractListimplements:List, RandomAccess, Cloneable, java.io.Serializable

2. 全局变量

(1) 存放数据的数组

protected Object[] elementData;

(2) 存放数量

protected int elementCount;

(3) 容量增量

protected int capacityIncrement;

3. 构造方法

(1) 不带参数的构造方法

publicVector(){this(10);}

(2) 带初始化容量大小的构造方法

/**

* @param initialCapacity 初始化容量大小

*/publicVector(intinitialCapacity){this(initialCapacity,0);}

(3) 带初始化容量和容量增量的构造方法

/** *@paraminitialCapacity初始化容量大小 *@paramcapacityIncrement 容量增量 */publicVector(intinitialCapacity,intcapacityIncrement){super();if(initialCapacity <0)thrownewIllegalArgumentException("Illegal Capacity: "+ initialCapacity);this.elementData =newObject[initialCapacity];this.capacityIncrement = capacityIncrement;}(4) 带Colleciton参数的构造方法/** *@paramc 将c转为Vector */publicVector(Collection 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);}

4. 方法

(1) public synchronized void copyInto(Object[] anArray)

源码解释:

通过JNI调用c++库的arraycopy方法,实现将anArray数组复制到当前的Vector。

publicsynchronizedvoidcopyInto(Object[] anArray){ System.arraycopy(elementData,0, anArray,0, elementCount); }

(2) public synchronized void trimToSize()

源码解释:

用以优化Vector的内存,我们都知道,Vector的每次容量增量是当前大小的2倍,但是当我们没法用完申请的这么多内存时,我们可以通过调用这个方法用以将不需要的内存释放掉。

publicsynchronizedvoidtrimToSize(){ modCount++;// 修改次数增加intoldCapacity = elementData.length;// 获取到当前的申请的内存大小if(elementCount < oldCapacity) {// 如果当前存放的数量比申请的内存要少elementData = Arrays.copyOf(elementData, elementCount);// 重新为这个数组申请elementCount大小内存,并存放这些数据} }

(3) public synchronized void ensureCapacity(int minCapacity)

源码解释:

当要扩容时,会调用此方法,保证当前容量能存放得下所需要存放的元素数量。如果不够时,会调用grow()方法进行扩容。

publicsynchronizedvoidensureCapacity(intminCapacity){if(minCapacity >0) { modCount++; ensureCapacityHelper(minCapacity); } }privatevoidensureCapacityHelper(intminCapacity){// overflow-conscious codeif(minCapacity - elementData.length >0) grow(minCapacity);// 如果当前容量比数组的长度要小时,调用grow扩容}privatestaticfinalintMAX_ARRAY_SIZE = Integer.MAX_VALUE -8;/** * Vector的扩容方法, 每次扩容至2倍 *@paramminCapacity */privatevoidgrow(intminCapacity){// overflow-conscious codeintoldCapacity = elementData.length;// 获取到数组的长度intnewCapacity = oldCapacity + ((capacityIncrement >0) ? capacityIncrement : oldCapacity);//如果没有初始化capacityIncrement,则增加至两倍if(newCapacity - minCapacity <0) newCapacity = minCapacity;if(newCapacity - MAX_ARRAY_SIZE >0)// 如果扩容后的容量超过了最大容量newCapacity = hugeCapacity(minCapacity);// 调用hugeCapacity方法elementData = Arrays.copyOf(elementData, newCapacity);// 将数组复制到扩容后的新内存中去}privatestaticinthugeCapacity(intminCapacity){if(minCapacity <0)// overflowthrownewOutOfMemoryError();return(minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }

(4) public synchronized void setSize(int newSize)

源码解释:

修改容量,当newSize比数组的长度要大时,将其复制到新的内存区域,如果要小的话,则从newSize位置到数组的最后一个位置的所有元素置为空。

publicsynchronizedvoidsetSize(intnewSize){ modCount++;if(newSize > elementCount) { ensureCapacityHelper(newSize); }else{for(inti = newSize ; i < elementCount ; i++) { elementData[i] =null; } } elementCount = newSize; }

(5) public synchronized int capacity()

源码解释:

返回数组长度,即申请内存的长度。

publicsynchronizedintcapacity(){returnelementData.length; }

(6) public synchronized int size()

源码解释:

返回数组已经使用的长度,即存放的数据个数。

publicsynchronizedintsize(){returnelementCount; }

(7) public synchronized boolean isEmpty()

源码解释:

判空,如果数量为0,即为empty。

publicsynchronizedbooleanisEmpty(){returnelementCount ==0; }

(8) public Enumeration<E> elements()

源码解释:

返回一个Enumeration对象的序列。Enumeration只有两个方法,hasMoreElements()和nextElement(),它只能从首个元素遍历到最后一个元素,并不能根据位置拿到具体的元素。

publicEnumeration elements() {returnnewEnumeration() {intcount=0;publicbooleanhasMoreElements() {returncount< elementCount; }publicE nextElement() {synchronized(Vector.this) {if(count< elementCount) {returnelementData(count++); } }thrownewNoSuchElementException("Vector Enumeration"); } }; }

(9) public boolean contains(Object o)

源码解释:

是否包含对象o。调用indexOf判断是否存在。

publicbooleancontains(Object o){returnindexOf(o,0) >=0; }publicintindexOf(Object o){returnindexOf(o,0); }

(10) public synchronized int indexOf(Object o, int index)

源码解释:

判断o是否为空,如果为空,则遍历是否存在值为空的元素;不为空,判断是否存在和o相等的元素。

publicsynchronizedintindexOf(Object o,intindex) {if(o ==null) {for(inti =index; i < elementCount ; i++)if(elementData[i]==null)returni; }else{for(inti =index; i < elementCount ; i++)if(o.equals(elementData[i]))returni; }return-1; }

(11) public synchronized int lastIndexOf

源码解释:

获取该元素所处的最后一个位置,不存在则返回-1

publicsynchronizedintlastIndexOf(Object o){returnlastIndexOf(o, elementCount-1); }publicsynchronizedintlastIndexOf(Object o,intindex){if(index >= elementCount)thrownewIndexOutOfBoundsException(index +" >= "+ elementCount);if(o ==null) {for(inti = index; i >=0; i--)if(elementData[i]==null)returni; }else{for(inti = index; i >=0; i--)if(o.equals(elementData[i]))returni; }return-1; }

(12) public synchronized E elementAt(int index)

源码解释:

返回这个位置的元素。其实就是判断数组的index位置是否有元素

publicsynchronized E elementAt(intindex) {if(index>= elementCount) {thrownewArrayIndexOutOfBoundsException(index+" >= "+ elementCount); }returnelementData(index); } E elementData(intindex) {return(E) elementData[index]; }

(13) public synchronized E firstElement()

源码解释:

返回数组第0个位置的元素。

publicsynchronizedEfirstElement(){if(elementCount ==0) {thrownewNoSuchElementException(); }returnelementData(0); }

(14) public synchronized E lastElement()

源码解释:

返回数组最后一个位置的元素。

publicsynchronizedElastElement(){if(elementCount ==0) {thrownewNoSuchElementException(); }returnelementData(elementCount -1); }

(15) public synchronized void setElementAt(E obj, int index)

源码解释:

修改第index位置的值为obj。其实就是数组赋值。

publicsynchronizedvoidsetElementAt(E obj,intindex) {if(index>= elementCount) {thrownewArrayIndexOutOfBoundsException(index+" >= "+ elementCount); } elementData[index] = obj; }

(16) public synchronized void removeElementAt(int index)

源码解释:

获取到index位置后有多少个元素,并将index位置后面的元素复制到index位置前的后面,再将index位置置空。复制操作通过JNI实现。

publicsynchronizedvoidremoveElementAt(intindex) { modCount++;if(index>= elementCount) {thrownewArrayIndexOutOfBoundsException(index+" >= "+ elementCount); }elseif(index<0) {thrownewArrayIndexOutOfBoundsException(index); }intj = elementCount -index-1;// 获取第index位置后有多少个元素if(j >0) { System.arraycopy(elementData,index+1, elementData,index, j);// 将emlementData[j, elementCount]复制到elementData[0, index]的后面} elementCount--; elementData[elementCount] =null;// 等待GC}

(17) public synchronized void insertElementAt(E obj, int index)

源码解释:

将index位置后的元素复制到原数组的index+1位置后的地方,并在index位置赋值obj。

publicsynchronizedvoidinsertElementAt(E obj,intindex) { modCount++;if(index> elementCount) {thrownewArrayIndexOutOfBoundsException(index+" > "+ elementCount); } ensureCapacityHelper(elementCount +1);// 判断是否需要扩容System.arraycopy(elementData,index, elementData,index+1, elementCount -index);// 将elementData[index+1, elementCount]复制到elementData[index+1, elementCount +1]位置,elementData[index] = obj;// 在index位置赋值objelementCount++; }

(18) public synchronized void addElement(E obj)

源码解释:

在数组最后添加数据obj。先扩容,再通过数组赋值。

publicsynchronized voidaddElement(E obj) { modCount++;ensureCapacityHelper(elementCount +1);elementData[elementCount++] = obj;}

(19) public synchronized boolean removeElement(Object obj)

源码解释:

移除元素。先获取到移除元素在数组的位置,然后调用removeElementAt方法移除元素。

publicsynchronizedbooleanremoveElement(Object obj){ modCount++;inti = indexOf(obj);if(i >=0) { removeElementAt(i);returntrue; }returnfalse; }

(20) public synchronized void removeAllElements()

源码解释:

移除所有元素。即将所有元素置为null,等待gc。

publicsynchronizedvoidremoveAllElements(){ modCount++;// Let gc do its workfor(inti =0; i < elementCount; i++) elementData[i] =null;  elementCount =0; }

(21) public synchronized E get(int index)

源码解释:

获取index位置的元素。

publicsynchronized E get(intindex) {if(index>= elementCount)thrownewArrayIndexOutOfBoundsException(index);returnelementData(index); }

(22) public synchronized E set(int index, E element)

源码解释:

修改index位置的值为element。实现原理也是直接数组赋值。

public synchronized E set(intindex,Eelement) {if(index>= elementCount) throw new ArrayIndexOutOfBoundsException(index);  E oldValue = elementData(index); elementData[index] =element; return oldValue; }

(23) public synchronized boolean add(E e)

源码解释:

在最后位置新增元素e。先判断是否需要扩容,然后赋值。实现原理和addElement是一样的。

publicsynchronized boolean add(E e) { modCount++;ensureCapacityHelper(elementCount +1);elementData[elementCount++] = e;return true;}

(24) public boolean remove(Object o)

源码解释:

移除元素,和removeElement方法一样。

public boolean remove(Object o) {

return removeElement(o);

}

(25) public void add(int index, E element)

源码解释:

添加元素,和insertElementAt方法一样。

public void add(intindex,Eelement) { insertElementAt(element,index); }

(26) public synchronized E remove(int index)

源码解释:

移除index位置的元素。实现思路和removeElementAt类似。

public synchronized E remove(intindex) { 

modCount++;

if(index>= elementCount)thrownewArrayIndexOutOfBoundsException(index); 

E oldValue = elementData(index);

// 获取到旧值

intnumMoved = elementCount -index-1;

// index位置后面元素的个数

if(numMoved >0) System.arraycopy(elementData,index+1, elementData,index, numMoved);

// 将elementData[index+1, elementCount]移到element[0, index]后elementData[--elementCount] =null;

// Let gc do its workreturnoldValue; 

}

(27) public void clear()

源码解释:

移除所有元素,直接调用removeAllElements()。

publicvoidclear(){ removeAllElements(); }

(28) public synchronized boolean containsAll(Collection<?> c)

源码解释:

判断是否包含集合c的所有元素。调用AbstractCollection的实现,代码也很简单,不赘叙。

publicsynchronizedbooleancontainsAll(Collection<?> c){returnsuper.containsAll(c); }publicbooleancontainsAll(Collection<?> c){for(Object e : c)if(!contains(e))returnfalse;returntrue; }

(29) public synchronized boolean addAll(Collection<? extends E> c)

源码解释:

把集合c复制到当前数组的末尾。先判断是否需要扩容,然后调用JNI的arraycopy实现复制。

publicsynchronized 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; }

(30) public synchronized boolean removeAll(Collection<?> c)

源码解释:

将包含集合c的所有元素移除。调用AbstractCollection的实现,代码也很简单,不赘叙。

publicbooleanremoveAll(Collection c) { Objects.requireNonNull(c);booleanmodified =false; Iteratorit= iterator();while(it.hasNext()) {if(c.contains(it.next())) {it.remove(); modified =true; } } returnmodified; }

(31) public synchronized boolean retainAll(Collection<?> c)

源码解释:

将数组中不是c中包含的元素全部移除。调用AbstractCollection的实现,代码也很简单,不赘叙。

publicsynchronizedbooleanretainAll(Collection<?> c){returnsuper.retainAll(c); }publicbooleanretainAll(Collection<?> c){ Objects.requireNonNull(c);booleanmodified =false; Iterator it = iterator();while(it.hasNext()) {if(!c.contains(it.next())) { it.remove(); modified =true; } }returnmodified; }

(32) public synchronized boolean addAll(int index, Collection<? extends E> c)

源码解释:

在index位置插入集合c。实现原理和在某个位置插入元素原理一样,不赘叙。

publicsynchronizedbooleanaddAll(intindex, Collection c) { modCount++;if(index<0||index> elementCount)thrownewArrayIndexOutOfBoundsException(index);  Object[] a = c.toArray();intnumNew = a.length; ensureCapacityHelper(elementCount + numNew);intnumMoved = elementCount -index;if(numMoved >0) System.arraycopy(elementData,index, elementData,index+ numNew, numMoved);  System.arraycopy(a,0, elementData,index, numNew); elementCount += numNew;returnnumNew !=0; }

(33) public synchronized List<E> subList(int fromIndex, int toIndex)

源码解释:

调用父类的subList,然后通过Collections.synchronizedList来保证子List是同步的,这也就印证了我们前面所说的Collections.synchronizedList初始化的ArrayList和Vector是一样效率的,因为它们的同步方式都是一样的,而增删改查这些操作对于它们两个来说都是一样的原理,所以可以知道它们的效率是一样的。

publicsynchronizedListsubList(intfromIndex,inttoIndex){returnCollections.synchronizedList(super.subList(fromIndex, toIndex),this); }

(34) protected synchronized void removeRange(int fromIndex, int toIndex)

源码解释:

将某个范围的数据移除。实现原理和删除某个位置的元素原理是一样的,不赘叙。

protectedsynchronizedvoidremoveRange(intfromIndex,inttoIndex){ modCount++;intnumMoved = elementCount - toIndex; System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);// Let gc do its workintnewElementCount = elementCount - (toIndex-fromIndex);while(elementCount != newElementCount) elementData[--elementCount] =null; }

(35) public synchronized ListIterator<E> listIterator(int index)

源码解释:

返回一个从index位置开始的LIstIterator,方便我们遍历Vector,关于ListIterator在《Java容器类源码-LinkedList的最全的源码分析》已经详说,这里不赘叙。

publicsynchronized ListIterator listIterator(intindex) {if(index<0||index> elementCount)thrownewIndexOutOfBoundsException("Index: "+index);returnnewListItr(index); }

(36) public synchronized ListIterator<E> listIterator()

源码解释:

返回一个从0位置开始的ListIterator,不赘叙。

publicsynchronizedListIteratorlistIterator(){returnnewListItr(0); }

(37) public synchronized Iterator<E> iterator()

源码解释:

返回一个Iterator实现类Itr。有人会问ListIterator和Itr有什么区别吗?其实ListIterator是Itr的子类,它在Itr的基础上再增加了一些接口,例如hasPrevious(),nextIndex()等,所以如果觉得Iterator不能满足你的需求,可以看一下ListIterator里面提供的API。

publicsynchronizedIteratoriterator(){returnnewItr(); }

(38) public Spliterator<E> spliterator()

源码解释:

实例化一个VectorSpliterator对象,并返回。VectorSpliterator是JDK1.8之后LinkedList新增的内部类,因为用得比较少,我就不在这里班门弄斧了,大家有需要可以自行深入研究。

publicSpliteratorspliterator(){returnnewVectorSpliterator<>(this,null,0,-1,0); }

四、总结

看完了Vector的源码,我觉得我们需要学到比较重要的几点。首先是开头所说的Vector和ArrayList的区别,这里就不重复了。第二个就是我们通过subList这个实现可以看到,它的子序列其实也是通过Collections.synchronizedList来初始化子序列并返回的,所以其实Collections.synchronizedList初始化的ArrayList实现同步的原理和Vector是一样的,而ArrayList和Vector的底层都是数组,常规的增删改查操作是一样的,所以我们可以确定Vector和实现了同步的ArrayList在数据操作时的效率是相近的,所以我觉得我们并不需要纠结在考虑线程安全时到底是用Collections.synchronizedList初始化的ArrayList还是Vector。第三个就是需要了解到Vector的增删改查实现原理,它的api核心可以说就是这几个方法了,所有其他api都是围绕这几个方法来进行扩展。

---------------------

每天都在分享文章,也每天都有人想要我出来给大家分享下怎么去学习Java。大家都知道,我们是学Java全栈的,大家就肯定以为我有全套的Java系统教程。没错,我是有Java全套系统教程,进扣裙【47】974【9726】所示,进群的时候记得表明自己想要学习什么,不要用小号,这样小编才好给你们发定向资源,今天小编就免费送!~

“我们相信人人都可以成为一个程序员,现在开始,找个师兄,带你入门,学习的路上不再迷茫。这里是ja+va修真院,初学者转行到互联网行业的聚集地。" public synchronized E remove(int index) {

modCount++;

if (index >= elementCount)

throw new ArrayIndexOutOfBoundsException(index);

E oldValue = elementData(index); // 获取到旧值

int numMoved = elementCount - index - 1; // index位置后面元素的个数

if (numMoved > 0)

System.arraycopy(elementData, index+1, elementData, index, numMoved); // 将elementData[index+1, elementCount]移到element[0, index]后

elementData[--elementCount] = null; // Let gc do its work

return oldValue;

}

(27) public void clear(

相关文章

网友评论

    本文标题:Java容器类源码-Vector的最全的源码分析

    本文链接:https://www.haomeiwen.com/subject/cfowrqtx.html