这一篇文章算是从java基础性文章结束到进阶的一个过渡,虽然自己从未使用过Vector这样的容器进行数据的增删改查操作,但还是按照一贯的思路进行分析一下它的源码。
先简单看下它的结构图,然后看下提供的方法都有哪些,逐步分析每个方法的使用和源码。
默认的无参构造函数,默认初始容量为10。也就是说创建了一个空的vector它的容量为10,内存分配可装载数据大小为10个元素。
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;
}
先暂时看下vector的add方法的使用,为什么vector容器是线程安全的?因为方法上都加了synchronized关键字进行锁定,保证了数据的可见性,一致性,顺序性的特点。
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);//进行数组的动态扩容机制
elementData[elementCount++] = e;//在数组的末尾进行添加元素,时间复杂度为O(1)
return true;
}
步骤一: 判断数据是否大于数组的长度
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
步骤二:进行数据的扩容机制
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//数据之间的拷贝以及引用的指向
}
步骤三: 进行最大容量的确定,这里也会出现OOM现象
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
为什么ensureCapacityHelper()这个方法没有加锁,主要是为了体现锁粗化的实现,什么是锁粗化?外围方法已经加了锁,里面没有必要再加锁了,后面介绍内容时会对其进行介绍的,这里先暂时说到这吧。
统计数据集合的元素个数:size()方法。
public synchronized int size() {
return elementCount;//表示当前集合元素有多少
}
获取指定元素位置的元素:get()方法。
public synchronized E get(int index) {
//快速失败机制的完美体现哈
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);//根据索引index下标获取元素,因为数组支持
}
判断数据集合是否为空:isEmpty()方法。
public synchronized boolean isEmpty() {
return elementCount == 0;//二元运算符,判断集合元素个数是否等于0
}
获取数据集合指定元素的索引位置:indexOf()方法。
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;
}
上面主要体现了java设计者优秀的能力,由面向过程编程编程了面向对象 编程的体现。
获取数据集合的第一个元素:firstElement()方法。
public synchronized E firstElement() {//加锁
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(0);
}
这里如果没有集合元素,但是去查找了就会抛出一个运行时异常。
public class NoSuchElementException extends RuntimeException {}
获取数据集合的最后一个元素:lastElement()方法。
public synchronized E lastElement() {
if (elementCount == 0) {
throw new NoSuchElementException();
}
return elementData(elementCount - 1);//由于数组下标是从0开始的,所以最后一个元素的下标为elementCount-1
}
数据集合指定位置的元素替换set()方法。
public synchronized E set(int index, E element) {
//快速失败机制
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;//返回旧值元素
}
移除指定索引位置的元素: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--;//数据元素个数减一,gc进行"垃圾"回收,具体回收时间确定
elementData[elementCount] = null; /* to let gc do its work */
}
在指定位置设置指定元素:setElementAt()方法。
public synchronized void setElementAt(E obj, int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
elementData[index] = obj;//将指定元素在对应的索引位置进行替换
}
移除数据集合中的元素:removeElement()方法。
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);//确定要移除元素的索引位置
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
移除数据集合的所有元素:removeAllEmenets()方法。
public synchronized void removeAllElements() {
modCount++;//这里主要是为了防止并发修改产生的问题
// Let gc do its work
for (int i = 0; i < elementCount; i++)
elementData[i] = null;//gc进行垃圾收集,收集时间不确定
elementCount = 0;//将集合元素个数置为0
}
移除数据集合的所有元素:clear()方法。
public void clear() {
removeAllElements();
}
上面的基本方法,其用法和写法都很优秀,值得学习,后面进行总结的时候会慢慢输出自己对其的理解,如有不当之处还望指正。接下来我们继续看下其它比较好的设计。
或许曾经我们用过迭代器这种方法来进行数据的获取和遍历集合元素,接下来我们看下Vector是如何获取迭代器进行数据遍历。
public synchronized Iterator<E> iterator() {
return new Itr();
}
步骤一:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
//检查是否还存在下一个元素
public boolean hasNext() {
return cursor != elementCount;
}
public E next() {
synchronized (Vector.this) {//锁住当前字节码对象.class
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;
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
synchronized (Vector.this) {
final int size = elementCount;
int i = cursor;
if (i >= size) {
return;
}
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) Vector.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
action.accept(elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}
//检查是否存在并发修改的问题
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
由于现在的集合都支持了Stream流式处理,所以我们这里看下Vector是如何进行元素遍历的。
public synchronized void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);//判断consumer是否为空
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int elementCount = this.elementCount;
for (int i=0; modCount == expectedModCount && i < elementCount; i++) {
action.accept(elementData[i]);//accept接收元素,然后输出
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
这里我们继续看下vector是如何对元素进行排序的。
public synchronized void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, elementCount, c);
if (modCount != expectedModCount) {//防止并发修改
throw new ConcurrentModificationException();
}
modCount++;
}
步骤一:
public static <T> void sort(T[] a, int fromIndex, int toIndex,
Comparator<? super T> c) {
if (c == null) {//判断是否存在排序规则,不存在执行sort
sort(a, fromIndex, toIndex);
} else {//存在,则基于不同的排序算法进行处理,关于排序的代码这里暂不做分析
rangeCheck(a.length, fromIndex, toIndex);
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, fromIndex, toIndex, c);
else
TimSort.sort(a, fromIndex, toIndex, c, null, 0, 0);
}
}
判断集合是否包含某个元素:contains()方法。
public boolean contains(Object o) {
return indexOf(o, 0) >= 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;
}
到这里Vector这个集合容器,我这里就梳理完了,下面提供了一些如何追踪源码的示例程序,供参考。
package com.wpw.springbootjuc.java8.map;
import lombok.extern.slf4j.Slf4j;
import java.util.Vector;
/**
* vector源码走读
*
* @author wpw
*/
@Slf4j
public class VectorTest {
public static void main(String[] args) {
Vector vector = new Vector(10);
vector.add(1);
vector.add(2);
vector.add(3);
log.debug("[{}]数据集合的遍历", 1);
vector.forEach(System.out::println);
log.debug("[{}]返回数据集合元素的大小", 2);
int size = vector.size();
log.info("数据大小:[{}]", size);
log.info("获取集合元素指定位置[{}]的元素:[{}]", 0, vector.get(0));
log.info("判断数据集合是否为空:[{}]", vector.isEmpty());
log.info("获取数据集合指定元素的索引位置:[{}]", vector.indexOf(3));
log.info("获取数据集合的第一个元素:{{}}", vector.firstElement());
log.info("获取数据集合的最后一个元素:[{}]", vector.lastElement());
vector.set(2, 4);
vector.stream().forEach(System.out::println);
vector.removeElementAt(2);
vector.stream().forEach(x -> System.out.print(x + "\t"));
System.out.println();
vector.setElementAt(4, 1);
vector.stream().forEach(x -> System.out.print(x + "\t"));
System.out.println();
vector.removeElement(4);
vector.stream().forEach(x -> System.out.print(x + "\t"));
System.out.println();
vector.clear();
vector.stream().forEach(System.out::println);
boolean flag = vector.contains(1);
System.out.println("flag = " + flag);
}
}
分析了Vector学习到了很多东西,在以后的源码分析过程中,自己也会慢慢总结分享出来,如有不当之处还望指正。
最后附上一张部分Vector容器集合的方法截图和Vector继承关系描述。
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{}