JDK源码阅读之Vector

Vector

      Vector与ArrayList十分相似,只是ArrayList【读我】是线程不安全的,而Vector的实现是线程安全的。现在一起来看看它的实现吧!

类图

Vector类图

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

      可以发现Vector继承的类和实现的接口与ArrayList一模一样。

类前注释

      由iterator()listIterator(int)方法返回的迭代器是fail-fast的:如果列表在迭代器创建之后的任何时间被结构化地修改,除了通过迭代器自己的remove或add方法之外,迭代器将会抛出ConcurrentModificationException。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。由elements()返回的Enumerations 不是fail-fast的。

      Vector是同步的。 如果不需要线程安全的实现,建议使用ArrayList代替Vector。

成员变量

/*
 * 顾名思义,此变量用于存储Vector的数据,同时我们也知道了Vector底层也是数组实现的。
 * Vector的容量是此数组缓冲区的长度,并且至少足够大以包含所有向量的元素。
 * 注:Vector中最后一个元素后面的任何数组元素都为空。
 */
protected Object[] elementData;

/*
 * 该Vector对象中有效组件的数量。 组件elementData[0]至elementData[elementCount-1]是实际项目。
 */
protected int elementCount;

/*
 * 当Vector的大小大于其容量时,Vector的容量自动增加的量。
 * 如果容量增量小于或等于零,则每次需要增长时,向量的容量将加倍。
 */
protected int capacityIncrement;

构造方法

public Vector() {
	this(10);
}

public Vector(int initialCapacity) {
	this(initialCapacity, 0);
}

public Vector(int initialCapacity, int capacityIncrement) {
	super();
	if (initialCapacity < 0)
		throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
	this.elementData = new Object[initialCapacity];
	this.capacityIncrement = capacityIncrement;
}

      通过构造方法我们知道如果使用无参构造方法默认初始化容量大小为10,且每次扩容时向量的容量将加倍(因为capacityIncrement<=0)。指定的初始化容量必须大于等于0,否则抛异常。

public Vector(Collection<? extends E> c) {
	elementData = c.toArray();
	elementCount = elementData.length;
	// c.toArray也许不会返回Object[]类型
	if (elementData.getClass() != Object[].class)
		elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

      按照集合的迭代器返回的顺序,构造一个包含指定集合元素的向量。

copyInto

public synchronized void copyInto(Object[] anArray) {
	System.arraycopy(elementData, 0, anArray, 0, elementCount);
}

      将此Vector中的组件复制到指定的数组中。Vector中索引k处的项目被复制到anArray的索引k处。可以发现此方法使用synchronized修饰了,所以这是线程安全的,我们可以发现其实Vector中大量方法大部分是使用它完成同步的,所以Vector的效率其实是不高的。

trimToSize

public synchronized void trimToSize() {
	modCount++;
	int oldCapacity = elementData.length;
	if (elementCount < oldCapacity) {
		elementData = Arrays.copyOf(elementData, elementCount);
	}
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
	@SuppressWarnings("unchecked")
	T[] copy = ((Object)newType == (Object)Object[].class)
		? (T[]) new Object[newLength]
		: (T[]) Array.newInstance(newType.getComponentType(), newLength);
	System.arraycopy(original, 0, copy, 0,
					 Math.min(original.length, newLength));
	return copy;
}

      修改该Vector的容量为Vector的当前大小。底层通过新建一个大小为Vector大小的数组并将原数据拷贝过去来实现,所以elementData变化了。

grow

      此方法用于Vector的扩容。在与检查容量相关方法中都需要使用此方法。

private void grow(int minCapacity) {
	//minCapacity表示所需的最小容量
	int oldCapacity = elementData.length;
	//通过此行代码可以知道如果capacityIncrement<=0,则双倍扩容。
	int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
	//如果capacityIncrement扩容后的新容量依旧小于minCapacity,则新容量就是minCapacity
	if (newCapacity - minCapacity < 0)
		newCapacity = minCapacity;
	//扩容后的新容量大于MAX_ARRAY_SIZE,我们需要重新计算容量
	//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	if (newCapacity - MAX_ARRAY_SIZE > 0)
		newCapacity = hugeCapacity(minCapacity);
	elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
	if (minCapacity < 0) // overflow
		throw new OutOfMemoryError();
	return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

ensureCapacity

      确认Vector的容量是否满足要求(minCapacity表示所需的最小容量),过小则使用grow方法进行扩容。

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); //扩容
}

setSize

      设置Vector的大小。如果新的大小大于当前大小,则新的项目将添加到Vector的末尾。 如果新的大小小于当前尺寸,则将newSize后的所有元素置为null。

public synchronized void setSize(int newSize) {
	modCount++;
	if (newSize > elementCount) {
		//新大小大于当前大小,需要确认一下容量,判断是否需要扩容
		ensureCapacityHelper(newSize);
	} else {
		for (int i = newSize ; i < elementCount ; i++) {
			elementData[i] = null;//将newSize后的所有元素置为null
		}
	}
	elementCount = newSize;
}

capacity

      返回当前Vector的容量。

public synchronized int capacity() {
    return elementData.length;
}

size

      返回当前Vector包含项目的数目。

public synchronized int size() {
    return elementCount;
}

isEmpty

      判断当前Vector是否为空,为空返回true,否则返回false。

public synchronized boolean isEmpty() {
    return elementCount == 0;
}

elements

      返回Vector中元素的枚举。返回的Enumeration对象将生成Vector中的所有项。产生的第一项索引为0,第二项索引为1,依此类推。在遍历过程中我们可以之间进行添加和删除操作,说明其不提供fail-fast机制

indexOf

      返回Vector中指定元素的第一次出现位置的索引,如果此向量不包含元素,则返回-1。与此方法类似的还有lastIndexOf,只是此方法返回最后一次出现位置的索引。

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;
	}
	//找不到返回-1
	return -1;
}

elementAt

      返回指定索引的元素。与其类似的方法有firstElementlastElement,返回第一个元素和最后一个元素,还有get(int index)

setElementAt

      设置指定索引的元素。与之类似的有set(int index, E element)

removeElementAt

      删除指定索引的元素。removeElement(Object obj)方法调用此方法实现删除操作(先找位置再删除),remove(Object o)方法调用removeElement实现。

public synchronized void removeElementAt(int index) {
	modCount++;
	if (index >= elementCount) {
		throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
	}
	else if (index < 0) {
		throw new ArrayIndexOutOfBoundsException(index);
	}
	//计算index后面的元素个数
	int j = elementCount - index - 1;
	if (j > 0) {
		//将index后面的元素复制到前一位
		System.arraycopy(elementData, index + 1, elementData, index, j);
	}
	elementCount--;
	elementData[elementCount] = null; /* to let gc do its work */
}

remove

      删除指定索引位置的元素,返回旧值。

removeIf

      传入一个谓词,满足指定条件的元素。

public static void main(String[] args) {
	Vector<Integer> vector = new Vector<Integer> (100);
	vector.add(1);vector.add(2);
	vector.add(3);vector.add(4);
	vector.add(5);vector.add(6);
	vector.add(7);vector.add(8);
	boolean b = vector.removeIf(n -> n % 2 == 0);
	Iterator<Integer> iterator = vector.iterator();
	while (iterator.hasNext()){
		System.out.print(iterator.next() + " ");
	}
}
//1 3 5 7

removeAllElements

      删除Vector所有元素。底层将Vector中的所有元素置为null,一切就交给GC了。clear方法使用此方法完成。

insertElementAt

      往指定索引中插入元素。Vector的add(int index, E element)方法调用此方法实现。

public synchronized void insertElementAt(E obj, int index) {
	modCount++;
	if (index > elementCount) {
		throw new ArrayIndexOutOfBoundsException(index + " > " + elementCount);
	}
	//插入元素前需要确认还有容量
	ensureCapacityHelper(elementCount + 1);
	//将index后(包括index)的元素后移一位
	System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
	//设置index索引上的值为新值
	elementData[index] = obj;
	elementCount++;
}

addElement

      往Vector尾部添加元素,与之类似的方法是add(E e)方法,但是add方法有返回值(返回true)。

public synchronized void addElement(E obj) {
	modCount++;
	//添加前先确认容量
	ensureCapacityHelper(elementCount + 1);
	//直接设置最后一个位置为新对象
	elementData[elementCount++] = obj;
}

addAll

      不仅仅可以一个个添加元素,还可以直接添加整个集合呢。

public synchronized boolean addAll(Collection<? extends E> c) {
	modCount++;
	Object[] a = c.toArray();
	int numNew = a.length;
	//不多说,看看有位置没
	ensureCapacityHelper(elementCount + numNew);
	//将a中的元素全部拷贝到elementData中
	System.arraycopy(a, 0, elementData, elementCount, numNew);
	//更新elementCount
	elementCount += numNew;
	//如果numNew为0则返回false
	return numNew != 0;
}

迭代器

      Vector为我们提供了两种迭代器,分别为ListItr和Itr。使用ListItr迭代器我们可以完成遍历,添加和修改节点的操作,使用ListItr我们不仅可以正向遍历链表也可以反向遍历。
      使用Itr我们只能正向遍历,并且只提供删除操作,无法添加和修改节点。

文章同步【个人站】

原创文章 234 获赞 1294 访问量 23万+

猜你喜欢

转载自blog.csdn.net/qq_25343557/article/details/101988118