JDK源码阅读笔记之Vector
前言
现在是公元2018年,应该已经很少有人用Vector这个类了吧~反正我是没用过。作为一个从JDK 1.0版本就存在的元老级别成员,或许在一些远古级别的项目中还存在它的身影。但是还是止不住的好奇心,想对他的代码一探究竟,了解他的今世前生。
概述
Vector和ArrayList的功能是非常相似的。都是支持动态的扩充和减少数据。Vector可以线程安全的修改数据,但是也不支持在循环中修改数据,当发生这种情况的时候会抛出ConcurrentModificationException异常。Vector保有一个现在不推荐使用的循环结构Enumeration。这个类功能上已经被iterator代替了。很重要的一点就是Enumeration对循环中修改数据不会做检查,所以这会导致一些未定义的行为发生。从JDK 1.2版本开始,Vector实现了List接口,他也就是被归类到了Java集合框架中,在需要线程安全的场景中可以使用它。但是我们不推荐使用,因为相对于其他的线程安全的解决方案,Vector的线程安全是基于synchronized关键字来保证,对于synchronized这样的重型锁性能很有很大的影响,所以在性能敏感的项目中不推荐使用。
源码解读
继承类和接口
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
从代码可以看出,Vector继承了AbstractList类,同时实现了List接口。所以Vector是作为List接口的一个实现类,属于collection家族的医院。
实现RandomAccess,标示它具有随机访问的能力。
实现Cloneable,具有拷贝的能力(仅仅是浅拷贝)
实现java.io.Serializable,可以具有序列化的能力。
属性
- protected Object[] elementData 这个属性用来存储数据的。如果数据过多,就会进行扩容。
- protected int elementCount 这个属性用来标记Vector的元素数量。
- protected int capacityIncrement 这个属性用于扩充时,指定扩容大小
构造方法
- Vector(int initialCapacity, int 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;
}
initialCapacity决定了elementData初始化的大小,capacityIncrement决定了下一次扩容的大小。
Vector调用了父类的构造方法。其实父类的构造方法里面也是啥都没干。如果initialCapacity传入的是负数,那么不允许这种操作会抛出异常。
2. Vector(int initialCapacity)
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
如上面的代码,这个重载构造方法内部调用的仍然是第一个构造方法,只不过将capacityIncrement设为0
3. Vector()
public Vector() {
this(10);
}
如果不传任何参数的构造方法,默认的initialCapacity大小为10, capacityIncrement为0
其他方法
1. void copyInto(Object[] anArray)
public synchronized void copyInto(Object[] anArray) {
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
这个方法类似copy方法,功能是将Vector的数据拷贝的传入的anArray里面。重点是抛出的几个异常。如果anArray是null,会抛出NullPointerException。如果anArray的空间不足会抛出IndexOutOfBoundsException异常。如果Vector数据的类型和anArray不兼容会抛出ArrayStoreException异常。实现是通过原生方法,所以不过多讲解。
2. void trimToSize()
public synchronized void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (elementCount < oldCapacity) {
elementData = Arrays.copyOf(elementData, elementCount);
}
}
这个方法的作用是将Vector的空间和当前元素的数量一致。是一个减少空间大小的方法。
3. void ensureCapacity(int minCapacity)
public synchronized void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
modCount++;
if (minCapacity > elementData.length)
grow(minCapacity);
}
}
4. int capacity()
public synchronized int capacity() {
return elementData.length;
}
获取当前Vector的容量大小
5. int size()
public synchronized int size() {
return elementCount;
}
获取当前当前Vector存储的数据大小
6. boolean isEmpty()
public synchronized boolean isEmpty() {
return elementCount == 0;
}
判断当前Vector是否为empty。
7 Enumeration elements()
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的所有元素。我们可以看到是通过内部实例化一个Enumeration类实现的功能。这个类功能和Iterator是完全一致的,并且可以转换成Iterator。现在遍历元素已经都通过Iterator提供了。Fail-fast机制,以及可选择的remove方法。Enumeration作为历史遗留的产物,很少使用他了。
8. contains(Object o)
public boolean contains(Object o) {
return indexOf(o, 0) >= 0;
}
List集合的contains都是通过indexOf方法判断是否大于等于0来实现的。很好的实现了代码的复用。
9. indexOf(Object o)
public int indexOf(Object o) {
return indexOf(o, 0);
}
indexOf方法是找到希望元素o在Vector中的位置。通过代码我们发现调用的是lingerie重载方法。
10. indexOf(Object o, int 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;
}
这段代码在List类中基本上都是一样的。对于null元素在集合中查找第一个null元素。对于非null元素遍历查找第一个equal的元素。如果找不到就返回-1。
11. lastIndexOf(Object o)
public synchronized int lastIndexOf(Object o) {
return lastIndexOf(o, elementCount-1);
}
功能就是indexOf反过来,从后面
12. lastIndexOf(Object o, int index)
public synchronized int lastIndexOf(Object o, int index) {
if (index >= elementCount)
throw new IndexOutOfBoundsException(index + " >= "+ elementCount);
if (o == null) {
for (int i = index; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = index; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
整体逻辑和indexOf类似,但是是从后往前遍历。
13. E elementAt(int index)
public synchronized E elementAt(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
}
return elementData(index);
}
这个方法就是获取对应位置的元素。逻辑上和get方法是等价的。
14. E firstElement()
public synchronized E firstElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(0);
}
获取Vector的第一元素。如果没有就抛出NoSuchElementException异常。这个不是显示异常,所以不用捕获。
15. E lastElement()
public synchronized E lastElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(elementCount - 1);
}
返回Vectro的最后一个元素。如果没有就抛出NoSuchElementException异常。
16. void setElementAt(E obj, int index)
public synchronized void setElementAt(E obj, int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
elementData[index] = obj;
}
在对应位置设置元素。逻辑上等同于set方法
17.removeElementAt(int index)
public synchronized void removeElementAt(int index) {
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);
}
modCount++;
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
讲对应位置的元素从Vector中移除。为了提高效率List的移除操作基本上内部都使用 System.arraycopy直接将后续的元素拷贝过来。
18. void insertElementAt(E obj, int index)
public synchronized void insertElementAt(E obj, int index) {
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
modCount++;
final int s = elementCount;
Object[] elementData = this.elementData;
if (s == elementData.length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = obj;
elementCount = s + 1;
}
19. void addElement(E obj)
public synchronized void addElement(E obj) {
modCount++;
add(obj, elementData, elementCount);
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
elementCount = s + 1;
}
20. boolean removeElement(Object obj)
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
删除指定元素,首先通过indexof方法获取obj的位置,然后通过removeElementAt讲对应位置的元素删除。
21. void removeAllElements()
public synchronized void removeAllElements() {
final Object[] es = elementData;
for (int to = elementCount, i = elementCount = 0; i < to; i++)
es[i] = null;
modCount++;
}
循环将Vector的所有元素都置为null。
22. Object clone()
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);
}
}
Vector的拷贝是浅拷贝。所以记得要自己实现深拷贝的方式。
23 其他方法
剩下的方法主要是和ArrayList类似的增删改查就不重复写了,另外为了让Vector兼容Java 8的lambda表达式还增加了一些特定的方法来支持。这部分之后打算在函数式编程中讲解。
总结
虽然Vecotor我们基本不使用了,但是还是可以从他的源码中学到一些东西。这样三个主要的List实现类就都讲完了,接下里我们会进入Map的源码分析中。