Java源码学习--Vector
AbstractList的实现类我们常见的就是ArrayList和LinkedList两个,但是Vector也是其一个实现类,我几乎没有使用过这个类,这次就一并来学习一下其源码,看看其和另外两个出名的List相比有何区别。
一、重要属性
Vector的属性和ArrayList相比少了一些:
静态属性
- serialVersionUID:是一个final修饰的long类型,和ArrayList相同,在序列化和反序列化的时候使用
普通属性
- elementData:是一个Object[]类型的对象,其作用和ArrayList中的同名对象一样
- elementCount:是一个int类型的数据,其和ArrayList中的size属性作用一样
- capacityIncrement:是一个int类型的数据,这个属性是当elementCount的值到达了elementData的容量之后,进行扩容时扩大的容量,当这个值小于等于0时扩容后的容量为当前容量的二倍
二、构造器
Vector的构造器和ArrayList相比,逻辑简单了很多,如果您的记性好的话,应该会记得ArrayList中有两个属性EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA,而他俩的爱恨纠葛在ArrayList的不同的构造器重载以及后续一些方法的调用中都有体现。Vector则简单直白了很多,因为它没有这些花里胡哨的玩意。
Vector的构造器中只有两个是最终被调用的。
第一个
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
可以看到,平平无奇,朴素至极,就是初始化了elementData和capacityIncrement两个属性
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector() {
this(10);
}
可见这两个构造器都是回调的含两个参数的构造器。
第二个
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);
}
第二个构造器只不过是使用参数中的Collection来初始化属性。
三、重要方法
1. add
作为一个容器,其add方法是最常用的方法:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
这里的ensureCapacityHelper方法是用来检查是否需要扩容的(细节到后面再说),add方法可见就是一个数组的赋值操作,并无任何特点。
add(int index, E element)
该方法将元素添加到指定的位置:
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);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
可以看到insertElementAt中首先也要判断是否需要扩容;之后就是将index后面的元素通过System.arraycopy方法实现向后移动一个元素的位置;最后将需要添加的元素插入到对应的位置。该方法由于需要将index后面的元素整体移动一格,所以相对来说还是有一些耗时的。
addAll(Collection c)
有了前面两个方法的铺垫之后,addAll的登场便显得没那么神秘了:
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;
}
也是老套路先检查是否需要扩容;之后进行数据的批量添加,同样耗时点在于System.arraycopy
addElement
可能是由于本人太菜了,一直不能理解该方法和add方法同时存在的意义:
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
没错,你没有看错,其只是比add方法少了一个return ture
2. remove
remove方法和add方法是一对儿,也是容器类中必备的方法:
public boolean remove(Object o) {
return removeElement(o);
}
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
public int indexOf(Object o) {
return indexOf(o, 0);
}
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;
}
可以看到,remove方法比add方法要麻烦一点,光是找到需要remove掉的对象就需要一个for循环一个一个地找,而之后调用的removeElementAt不用看大家就能猜到干了些什么:
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后面的元素前移动一格;将最后一个元素设置为null
remove(int index)
该方法和removeElementAt方法的实现是一模一样的,只是多了一个return语句:
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);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
removeAllElements
public synchronized void removeAllElements() {
modCount++;
// Let gc do its work
for (int i = 0; i < elementCount; i++)
elementData[i] = null;
elementCount = 0;
}
该方法很简单,就是一个for循环将所有的元素赋值为null。
3. set、get方法
set方法和get方法也是一对,分别用来修改和获取数据:
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 E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
4. 其他方法
public synchronized void copyInto(Object[] anArray) {
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
简单粗暴,直接调用System.arraycopy方法将容器内的元素拷贝到参数中。
public synchronized void setSize(int newSize) {
modCount++;
if (newSize > elementCount) {
ensureCapacityHelper(newSize);
} else {
for (int i = newSize ; i < elementCount ; i++) {
elementData[i] = null;
}
}
elementCount = newSize;
}
很简单的功能:size大于元素个数的时候检查是否需要扩容;size小于元素个数的时候将超出size的置为null(注意虽然setSize此时的参数size更小,但是elementData属性的length是不会变小的,只是elementCount变小了,也即只存在扩容操作,不存在缩容的情况)
public synchronized int capacity() {
return elementData.length;
}
这个方法印象中ArrayList是没有提供的,该方法返回的是elementData的长度,其大于等于elementCount(size()方法的返回值)
public boolean contains(Object o) {
return indexOf(o, 0) >= 0;
}
public int indexOf(Object o) {
return indexOf(o, 0);
}
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
}
这三个方法也是形影不离的,很简单,都是for循环搞定。
5. 令人费解的方法
唉,又是一堆方法,个人感觉有些已有的方法和这些都已经重复了,但是不知道为何还要保留出来:
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return elementData(index);
}
该方法和get方法完全一样
public synchronized E firstElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(0);
}
public synchronized E lastElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(elementCount - 1);
}
这两个方法分别等价于get(0)与get(size())
public synchronized void setElementAt(E obj, int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
elementData[index] = obj;
}
该方法和set方法几乎一模一样,只是set方法需要返回修改前的数据
四、迭代器
Vector和ArrayList很想,其也有两个方法来获取迭代器:
public synchronized ListIterator<E> listIterator() {
return new ListItr(0);
}
public synchronized Iterator<E> iterator() {
return new Itr();
}
当然类比ArrayList不用想也知道Itr是ListItr的直接父类,子类相较于父类,添加了很多方法(内部维护了modCount一致)来提供更多的功能
1. Itr
Vector的Itr的实现和ArrayList的差不多,三个属性都是一模一样的,直接copy来:
- cursor:是一个int值,代表当前所在的位置,其范围是[0, elementCount]
- lastRet:是一个int值,默认为-1,代表最后一次调用next方法返回的数据的位置
- expectedModCount:为一个int值,初始化时赋值为了Vector的modCount
重要方法
public E next() {
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();
synchronized (Vector.this) {
checkForComodification();
Vector.this.remove(lastRet);
expectedModCount = modCount;
}
cursor = lastRet;
lastRet = -1;
}
如果你不限麻烦的话,可以发现和ArrayList相比,这几个方法的内容几乎是一模一样的,只是使用了synchronize修饰了一番。
2. ListItr
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;
}
}
唉,偷个懒,可以发现相较于其直接父类Itr,增加了previous、add、set三个方法;其细节和注意点和Itr是一以贯之的。
五、总结
可以看到Vector和ArrayList几乎是一个模子里刻出来的,Vector唯一能拿的出手的好像只有一点:其是线程安全的,这得益于其所有方法(或者是内部的代码块)都是用了synchronize修饰了的,但是这种简单粗暴的实现也宣告着Vector在高并发情况下的表现也是不尽如人意的。
网友评论