【学习笔记】JDK源码学习之Vector(附带面试题)
创始人
2024-04-26 03:40:41
0

【学习笔记】JDK源码学习之Vector(附带面试题)

什么是 Vector ?它的作用是什么?它的底层由什么组成?是否是线程安全的?

老样子,跟着上面的问题,我们层层深入了解 Vector 吧。

1、什么是Vector?

VetcorArrayList 相似,其内部都是通过一个容量能够动态增长的 数组 来实现的。不同点是 Vector 是线程安全的。因为其内部有很多同步代码快来保证线程安全.
JDK 中原文有一句注释:

Vector is synchronized. If a thread-safe implementation is not needed, it is recommended to use ArrayList in place of Vector

表示如果不使用线程安全则可以使用 ArrayList ,因其效率比 Vector 高出不少。但是 Vector 是线程安全的,所以使用是比较好的。

一般使用 Vector

Vector vector = new Vector<>();

2、Vector的实现接口和继承类

继承图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CwRl9nM6-1670855079344)(/Users/tiejiaxiaobao/Library/Application Support/typora-user-images/image-20221210175748310.png)]

通过源码来看:

public class Vectorextends AbstractListimplements List, RandomAccess, Cloneable, java.io.Serializable
{....
}

从上述源码中我们能发现 Vector 实现了 RandomAccess、Cloneable和Serialzable 等接口。

2.1 继承List类

Vector 继承了 List 类则可以使 Vector 能进行增删改查。底层则是使用 数组 : protected Object[] elementData;

2.2 实现RandomAccess接口

ArrayList 一样,实现 RandomAccess 接口可以增加随机遍历的效率,能大大提高访问 Vector 数组的时间。

具体可参考本篇文章: 地址

2.3 实现Serializable接口

为什么要实现 Serializable 接口呢?首先我们需要知道Java为什么需要序列化和反序列化。搞清楚这两个我们才能去理解为什么 Vector 要去实现 Serializable 这个接口。

之前在写 LinkedList 源码中曾写过具体的介绍,大家详情可以参考本篇文章嗷:地址 。

2.4 实现Cloneable接口

ArrayList 一样,实现了 Cloneable 接口,即 Vector 也可以实现浅拷贝和深度拷贝。

具体例子本篇就不过对赘述,详情可以参考本篇文章:地址 。

3、Vector中的变量、构造函数和重要方法

3.1 Vector中的变量

    /*** The array buffer into which the components of the vector are* stored. The capacity of the vector is the length of this array buffer,* and is at least large enough to contain all the vector's elements.** 

Any array elements following the last element in the Vector are null.** @serial*/protected Object[] elementData;/*** The number of valid components in this {@code Vector} object.* Components {@code elementData[0]} through* {@code elementData[elementCount-1]} are the actual items.** @serial*/protected int elementCount;/*** The amount by which the capacity of the vector is automatically* incremented when its size becomes greater than its capacity. If* the capacity increment is less than or equal to zero, the capacity* of the vector is doubled each time it needs to grow.** @serial*/protected int capacityIncrement;/** use serialVersionUID from JDK 1.0.2 for interoperability */private static final long serialVersionUID = -2767605614048989439L;

  • elementData : 存储元素的数组,初始容量为10
  • elementCount : 记录数组中元素个数
  • capacityIncrement : 自动扩容的大小,即当数组满了之后,就添加capacityIncrement个空间装载元素,如果 capacityIncrement<=0,则扩容时就扩容到目前Vector容量的两倍
  • serialVersionUID该类的序列化版本号

3.2 Vector中的构造函数

Vector 一共有四个构造函数:

  • 指定容量和增长系数
  • 指定容量
  • 不指定,使用默认容量值10
  • 指定集合初始化

3.2.1 Vector(int initialCapacity, int capacityIncrement):

指定容量和增长系数构造函数

源码:

    /*** Constructs an empty vector with the specified initial capacity and* capacity increment.** @param   initialCapacity     the initial capacity of the vector* @param   capacityIncrement   the amount by which the capacity is*                              increased when the vector overflows* @throws IllegalArgumentException if the specified initial capacity*         is negative*/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(指定容量的值) 是否小于0。如果小于零就抛出 IllegalArgumentException 异常。
  • 然后初始化数组 elementData
  • 接着指定增长系数 capacityIncrement

3.2.2 Vector(int initialCapacity)

指定容量

源码:

    /*** Constructs an empty vector with the specified initial capacity and* with its capacity increment equal to zero.** @param   initialCapacity   the initial capacity of the vector* @throws IllegalArgumentException if the specified initial capacity*         is negative*/public Vector(int initialCapacity) {this(initialCapacity, 0);}

指定初始化容量,增长系数默认为0。

3.2.3 Vector()

源码:

    /*** Constructs an empty vector so that its internal data array* has size {@code 10} and its standard capacity increment is* zero.*/public Vector() {this(10);}

什么都不指定,默认给的容量是10。

3.2.4 Vector(Collection c)

指定集合初始化

源码:

    /*** Constructs a vector containing the elements of the specified* collection, in the order they are returned by the collection's* iterator.** @param c the collection whose elements are to be placed into this*       vector* @throws NullPointerException if the specified collection is null* @since   1.2*/public Vector(Collection c) {Object[] a = c.toArray();elementCount = a.length;if (c.getClass() == ArrayList.class) {elementData = a;} else {elementData = Arrays.copyOf(a, elementCount, Object[].class);}}

过程:

  • 先把集合 c 转化为 数组 a
  • 获取数组的长度并赋值给 elementCount
  • 判断如果是 ArrayList ,则直接复制 。
  • 否则需要进行拷贝。

3.3 Vector中重要的方法

3.3.1 add(E e):

源码:

    /*** Appends the specified element to the end of this Vector.** @param e element to be appended to this Vector* @return {@code true} (as specified by {@link Collection#add})* @since 1.2*/public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;}/*** This implements the unsynchronized semantics of ensureCapacity.* Synchronized methods in this class can internally call this* method for ensuring capacity without incurring the cost of an* extra synchronization.** @see #ensureCapacity(int)*/private void ensureCapacityHelper(int minCapacity) {// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}

过程:

  • 首先先修改 modCount 增加次数。
  • 判断容量是否足够。
  • 如果不够则进行扩容(下面单独分析 grow() 方法的作用)。
  • 反之将新元素放在最后一个元素,个数增加。

grow():

源码:

    /*** The maximum size of array to allocate.* Some VMs reserve some header words in an array.* Attempts to allocate larger arrays may result in* OutOfMemoryError: Requested array size exceeds VM limit*/private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;private void grow(int minCapacity) {// overflow-conscious code// 以前的容量int oldCapacity = elementData.length;// 现在的容量,是以前的容量加上扩展系数,如果扩展系数小于等于0,那么,就是以前的容量的两倍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);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}

过程:

  • 首先先获取之前的容量。
  • 然后在获取现在的容量:现在的容量,是以前的容量加上扩展系数,如果扩展系数小于等于0,那么,就是以前的容量的两倍。
  • 如果最小容量 minCapacity 小于 minCapacity 。则就按照最小容量来扩容。
  • 如果新的容量比最大的容量还要大(虚拟机的数组大小是有最大值的),则就抛出 OutOfMemoryError 异常。
  • 反之进行拷贝扩容。

3.3.2 add(int index, E element)

源码:

    /*** Inserts the specified element at the specified position in this Vector.* Shifts the element currently at that position (if any) and any* subsequent elements to the right (adds one to their indices).** @param index index at which the specified element is to be inserted* @param element element to be inserted* @throws ArrayIndexOutOfBoundsException if the index is out of range*         ({@code index < 0 || index > size()})* @since 1.2*/public void add(int index, E element) {insertElementAt(element, index);}/*** Inserts the specified object as a component in this vector at the* specified {@code index}. Each component in this vector with* an index greater or equal to the specified {@code index} is* shifted upward to have an index one greater than the value it had* previously.** 

The index must be a value greater than or equal to {@code 0}* and less than or equal to the current size of the vector. (If the* index is equal to the current size of the vector, the new element* is appended to the Vector.)**

This method is identical in functionality to the* {@link #add(int, Object) add(int, E)}* method (which is part of the {@link List} interface). Note that the* {@code add} method reverses the order of the parameters, to more closely* match array usage.** @param obj the component to insert* @param index where to insert the new component* @throws ArrayIndexOutOfBoundsException if the index is out of range* ({@code index < 0 || index > size()})*/public synchronized void insertElementAt(E obj, int index) {// 修改次数增加modCount++;// 判断index是否非法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
  • 增加 modCount 的修改次数。
  • 首先判断 index 是否越界。
  • 接着判断 Vector 中的容量是否够用。
  • 拷贝数据,将后面的元素,往后移动一位。
  • 将实际的数据插入。
  • 个数增加。

因为方法加了 synchronized 关键字可以保证线程安全,但是随之而来的就是效率的下降。

3.3.3 addAll(Collection c)

将一个集合所有元素添加进去

源码:

    /*** Appends all of the elements in the specified Collection to the end of* this Vector, in the order that they are returned by the specified* Collection's Iterator.  The behavior of this operation is undefined if* the specified Collection is modified while the operation is in progress.* (This implies that the behavior of this call is undefined if the* specified Collection is this Vector, and this Vector is nonempty.)** @param c elements to be inserted into this Vector* @return {@code true} if this Vector changed as a result of the call* @throws NullPointerException if the specified collection is null* @since 1.2*/public synchronized boolean addAll(Collection 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;}

过程:

  • 首先增加修改次数 modCount
  • 然后把 c 集合转化为数组并获取数组的长度。
  • 判断原来的数组长度加上现在的数组长度是否容量够用。
  • 如果够用则进行数据拷贝、增加数组长度并返回相关值。

3.3.4 addAll(int index, Collection c)

指定index,插入一个集合,和前面不一样的地方在于复制之前,需要计算往后面移动多少位,不是用for循环去插入,而是一次性移动和写入。

源码:

    /*** Inserts all of the elements in the specified Collection into this* Vector at the specified position.  Shifts the element currently at* that position (if any) and any subsequent elements to the right* (increases their indices).  The new elements will appear in the Vector* in the order that they are returned by the specified Collection's* iterator.** @param index index at which to insert the first element from the*              specified collection* @param c elements to be inserted into this Vector* @return {@code true} if this Vector changed as a result of the call* @throws ArrayIndexOutOfBoundsException if the index is out of range*         ({@code index < 0 || index > size()})* @throws NullPointerException if the specified collection is null* @since 1.2*/public synchronized boolean addAll(int index, Collection c) {// 增加修改次数modCount++;// 判读索引是否越界if (index < 0 || index > elementCount)// 如果越界则抛出异常throw new ArrayIndexOutOfBoundsException(index);// 将集合转化为数组Object[] a = c.toArray();// 获取新数组的长度int numNew = a.length;// 判断数组容量是否够用ensureCapacityHelper(elementCount + numNew);// 移动的步长计算int numMoved = elementCount - index;if (numMoved > 0)// 移动后面的元素,腾出位置给插入的元素System.arraycopy(elementData, index, elementData, index + numNew,numMoved);// 插入元素System.arraycopy(a, 0, elementData, index, numNew);// 更新个数elementCount += numNew;// 插入元素个数是否为0return numNew != 0;}

3.3.5 remove(Object o)

删除指定元素

源码:

    /*** Removes the first occurrence of the specified element in this Vector* If the Vector does not contain the element, it is unchanged.  More* formally, removes the element with the lowest index i such that* {@code (o==null ? get(i)==null : o.equals(get(i)))} (if such* an element exists).** @param o element to be removed from this Vector, if present* @return true if the Vector contained the specified element* @since 1.2*/// 实际调用的是removeElement()public boolean remove(Object o) {return removeElement(o);}// 线程安全public synchronized boolean removeElement(Object obj) {// 修改次数增加modCount++;// 获取第一个满足条件的元素缩影int i = indexOf(obj);// 如果索引满足条件if (i >= 0) {// 将索引为i的元素从数组中移除removeElementAt(i);// 返回删除成功的结果return true;}// 返回失败的结果return false;}// 线程安全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) {// 往前面移动一位(复制,覆盖)System.arraycopy(elementData, index + 1, elementData, index, j);}// 长度减1elementCount--;// 将最后一个元素指向null,最后GC会进行回收elementData[elementCount] = null; /* to let gc do its work */}

过程:

  • 首先找出最近的索引,然后判断索引是否越界。
  • 如果没有越界则获取该索引后的元素个数。
  • 把这些元素往前面移动一位(复制,覆盖)。
  • 原来最后一个元素的位置置空,让GC回收。

3.3.6 remove(int index)

按照索引删除元素

源码:

public synchronized E remove(int index) {// 修改次数增加modCount++;// 合法性判断if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);// 保存原来的数据E oldValue = elementData(index);// 移动的个数int numMoved = elementCount - index - 1;// 如果移动个数大于0if (numMoved > 0)// 后面的元素往前面移动一位,赋值,覆盖System.arraycopy(elementData, index+1, elementData, index,numMoved);// 最后一个元素置空elementData[--elementCount] = null; // Let gc do its work// 返回旧的元素return oldValue;}

过程:

  • 首先判断索引是否越界,如果越界则抛出 ArrayIndexOutOfBoundsException 异常。
  • 保存原来的数据,并获取要移动的元素个数。
  • 后面的元素往前面移动一位,赋值,覆盖。
  • 最后一个元素置空,并让GC回收。

3.3.7 set

下面两个set函数都是,修改索引为index的元素,区别就是一个会返回旧的元素,一个不会返回旧的元素。

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 void setElementAt(E obj, int index) {// 合法哦性判断if (index >= elementCount) {throw new ArrayIndexOutOfBoundsException(index + " >= " +elementCount);}// 直接更新elementData[index] = obj;}

3.3.8 get

源码:

public synchronized E get(int index) {// 合法判断if (index >= elementCount)throw new ArrayIndexOutOfBoundsException(index);// 返回数组的元素return elementData(index);}// 获取第一个元素: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);}E elementData(int index) {return (E) elementData[index];}

4、Vector常见的面试题

4.1 为什么要成倍的扩容而不是一次增加一个固定大小的容量呢?

4.2 为什么是以两倍的方式扩容而不是三倍四倍,或者其他方式呢?

4.3 Vector 和 ArrayList 的区别又有什么呢?

答案参考:地址

相关内容

热门资讯

叙事陷入循环——《犯罪心理第1... 叙事陷入循环——《犯罪心理第18季》 今天聊聊美剧《犯罪心理第18季》。 片名Criminal ...
巨亏、跌停、对赌纠纷,华夏幸福... (文/解红娟 编辑/张广凯) 从小幅下探到断崖式下跌,华夏幸福股价迎来三连跌停。 1月14日至1...
女教师群聊八卦被拘 律师解读网... 大象新闻记者 孔旭阳 林林 据中国新闻周刊报道,“95后”女教师林淼(化名)因在微信群里传播浙江台州...
2026民生关键词|推行长护险... 2025年12月召开的中央经济工作会议明确提出,“实施康复护理扩容提升工程,推行长期护理保险制度,加...
原创 “... 1月14日,湖南永州备受关注的“医生刘某赟涉嫌强奸两名未成年人案”再审落下帷幕。永州市中级人民法院最...
女子被判向“出轨丈夫”公开道歉... 1月15日,#女子被判向出轨丈夫道歉15天#话题登上微博热搜。 近日,河南女子牛娜在抖音连续多天...
从“育”到“托”,政策发力全程... 一段时间以来,湖南省长沙市通过建设托育综合服务中心与公办托育网络,扩大普惠性托位供给,切实减轻家庭养...
工行沧州分行被罚35万,涉保险... 蓝鲸新闻1月16日讯,近日,国家金融监督管理总局沧州监管分局发布了一张罚单,剑指中国工商银行股份有限...
2700余人“虚假住院” !成... 近日,四川成都公安机关对外公布,该市金牛区分局成功打掉一个特大医保诈骗犯罪团伙,该案涉及虚开住院记录...