Java集合系列-Vector内部探究

212 阅读3分钟

前言

在Java中集合使用的频率还是非常高、导致面试的常见问题、网上大部分的都有各种大佬做讲解的博客、本篇文章是根据网上资料、做的一个小总结。

JAVA版本1.8 1610451412355.png

上图为简图、具体基础图可以去idea中查看继承关系快捷键:ctrl+alt+u 从上图可以看到他顶级是Collection接口、接口方法说明所以说Vector实现了Collection接口中的方法。

抛出问题

  • Vector是一个线程安全的集合为什么?

Vector集合说明

创建一个Vector集合对象

Vector<Integer> list = new Vector<>();

List<Integer> list = new Vector<>();
// 创建一个集合、形式为多态、父类 = new 子类实现();

什么是多态:编译看左、运行看右。

属性

protected Object[] elementDatae; // 数组缓存区、说白了就是存放数据的地方
  
protected int elementCount; // 存放在elementData中的数据个数

protected int capacityIncrement; // Vector容量自动计算的量

构造器

public Vector(int initialCapacity, int capacityIncrement) {
  super();
  if (initialCapacity < 0) // 做了一个判断、判断初始容量是否小于0、如果小于则抛出一个非法容量的异常
    throw new IllegalArgumentException("Illegal Capacity: "+
                                       initialCapacity);
  this.elementData = new Object[initialCapacity];  // 创建对应大小的数组给elementData
  this.capacityIncrement = capacityIncrement; // capacityIncrement用来做扩容的判断的条件
}
// 设置开始容量的大小构造
public Vector(int initialCapacity) {
  this(initialCapacity, 0); // 然后又调用了新构造
}

// 无参构造
public Vector() {
  this(10); // 调了一个有参构造、这个数作为数组的初始容量
}

public Vector(Collection<? extends E> c) { // 将一个集合中的数据拷贝到Vector集合中
  elementData = c.toArray();
  elementCount = elementData.length;
  // c.toArray might (incorrectly) not return Object[] (see 6260652)
  if (elementData.getClass() != Object[].class)
  // 做为数据的拷贝赋值给elementData
    elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

对集合CRUD简单说明

public synchronized boolean add(E e) {
    modCount++; // 作为修改操作的记录数、在使用迭代的时候做条件判断后抛出ConcurrentModificationException异常
    ensureCapacityHelper(elementCount + 1);// 做扩充容量
    elementData[elementCount++] = e; // 添加到数组
    return true;
}

// ensureCapacityHelper 方法
private void ensureCapacityHelper(int minCapacity) {
  // overflow-conscious code
  if (minCapacity - elementData.length > 0) // 最小容量减去数组长度是否大于0
    grow(minCapacity); // 扩充的关键方法
}

// grow方法做为扩容核心
private void grow(int minCapacity) { 
  // overflow-conscious code 
  int oldCapacity = elementData.length; // 获取实际容量
  // 如果capacityIncrement大于0:int newCapacity = oldCapacity + capacityIncrement
  // 否则int newCapacity = oldCapacity + oldCapacity
  int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                   capacityIncrement : oldCapacity); // 扩充
  if (newCapacity - minCapacity < 0)  // 新容量减去最小容量 小于 0 这个判断的作用就是说明扩展后的新容量没有达到想要的容量大小、
    newCapacity = minCapacity;  // 如何将最小容量赋值给新容量
  if (newCapacity - MAX_ARRAY_SIZE > 0) // 这里做了一个判断、容量大小超过默认最大容量时的判断
    newCapacity = hugeCapacity(minCapacity); // 两种可能1、扩充更大的容量2、抛出一个内存溢出异常
  elementData = Arrays.copyOf(elementData, newCapacity); // 拷贝数组赋值给数组缓存区
}

private static int hugeCapacity(int minCapacity) {
  if (minCapacity < 0) // overflow //最小容量小于0 
    throw new OutOfMemoryError();  // 抛出内存溢出异常
  return (minCapacity > MAX_ARRAY_SIZE) ?
    Integer.MAX_VALUE :
  MAX_ARRAY_SIZE; // 返回更大的容量数量
}

小结:grow()方法做为Vector扩容核心、int newCapacity = oldCapacity + oldCapacity 简单理解是就10 + 10 = 20 、所以他的扩容是2倍、因为这个capacityIncrement > 0 添加不会成立。

public synchronized E remove(int index) {
    modCount++; // 作为修改操作的记录数、在使用迭代的时候做条件判断后抛出ConcurrentModificationException异常
    if (index >= elementCount) // 检查索引
        throw new ArrayIndexOutOfBoundsException(index); // 抛出下标异常
    E oldValue = elementData(index); // 获取元素内容
    //新数组的长度(数量)  =  数组数量  -  索引   -  1
    int numMoved = elementCount - index - 1;
    if (numMoved > 0) // 如果长度大于的0、就进行数组的复制。
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
                         // 帮助 垃圾回收(GC)
    elementData[--elementCount] = null; // Let gc do its work

    return oldValue; // 返回被删除的元素
}

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); // 获取对应下标数据
}

问题解决: Vector是一个线程安全的集合为什么?

  • synchronized:线程中的同步关键字用来修饰方法、作为同步方法、简单理解在多线程中是相斥、互不影响。 具体线程的知识、还请自行找相关的资料去学习。

Vector总结:

  • Vector和ArrayList源码差不多、默认容量都为10 、扩容的的倍数不同、Vector 2倍、ArrayList1.5倍、Vector是线程安全的集合而ArrayList是不安全的集合、他们的增删改都很消耗性能、ArrayList比Vector要快、因为因为ArrayList没有同步机制操作。

  • 扩容方式

    •  // Vector的
       int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
       //ArrayList的
       int newCapacity = oldCapacity + (oldCapacity >> 1);
      
  • Vector是可以重复元素也可以存放null值、线程安全集合。