Vector总结及部分底层源码分析

1. Vector继承的抽象类和实现的接口

  • Vector类实现的接口

    1. List接口:里面定义了List集合的基本接口,Vector进行了实现
    2. RandomAccess接口
    3. Cloneable接口
    4. Serializable:标记该类支持序列化
  • Vector继承了AbstractList抽象类

2. Vector底层的数据结构

  • Vector底层是基于数组实现的

    1. 特点:

      1. 数组在内存中是一个内存地址连续的,使用存储的元素也有序的,固定内存大小的内存空间,并且Vector可以存放重复的元素。允许存储 一个null或者多个null。
    2. 优点:

      1. 因为Vector在可能发送线程安全的方法上都添加了 synchronized 关键字进行了同步设置,所以Vector是线程安全的集合,在多线程的情况下可以直接使用。
      2. 因为使用了同步锁的原因在查询效率上不如ArrayList快,但是相比于链表而言查询效率还是快。
    3. 缺点:

      1. 当向Vector集合中存储的新的元素时,长度已经不够时,就会触发Vector底层的扩容机制。其实底层就是数组扩容。因为数组扩容需要把旧的数组元素复制到新的数组中,所以在数据量特别大的时候,扩容的效率不高
      2. 在指定位置添加和修改元素效率不高(尾部除外)。
        • 例如:在数组的尾部以外的地方插入一个元素,那么为例保证整个数组中所有元素的连续存储性,就必须向将从该位置到尾部的所有元素进行后移一位,腾出一个空的位置,再把新的元素进行插入。删除也是,删除了指定位置的之后需要把从该元素到尾部的所有元素向前移动一个位置。
      3. 虽然使用了 synchronized 保证线程安全,但是会损失掉一部分性能,所以目前已经基本弃用该集合。

3. Vector适用的场景

  1. 在多线程情况下,频繁对数据进行查找与修改的场景
  2. 但是为例将性能进行提升,可以创建线程安全ArrayList进行代替,使用Vector集合做的事情,使用ArrayList都可以做到,所以Vector基本已经不使用。
// 创建多线程环境下,线程安全的ArrayList集合,使用Collections集合工具类中的 synchronizedList() 方法
List<Object> list=Collections.synchronizedList(new ArrayList<Object>());

4. Vector底层源码分析

1. 四个构造函数

1. 无参构造函数

/**
* 默认无参构造函数
*/
public Vector() {
// 调用只有一个参数的构造函数,给参数一个定值10
this(10);
}

2. 有一个参数的构造函数

/**
* 有一个参数的构造函数
* @param initialCapacity 创建对象时如果使用默认的无参构造函数,默认大小为10
*/
public Vector(int initialCapacity) {
// 调用有两个参数的构造函数
this(initialCapacity, 0);
}

3. 有两个参数的构造函数

/**
* 有两个参数的构造函数,第一个参数代表数组的初始化长度,第二个是数组需要进行扩容的的增量值
* @param initialCapacity 集合的初始化长度大小,如果创建集合时采用无参构造器,则默认为10
* @param capacityIncrement Vector需要自动扩容时增加的容量值,不传递时默认为0
* @throws IllegalArgumentException 发生异常时抛出异常
*/
public Vector(int initialCapacity, int capacityIncrement) {
// 调用父类的构造函数
super();
// 判断传递进来的默认初始化集合长度大小的值是否小于0,如果小于则抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
// 创建一个Object类型的数组,长度为传递进来的 initialCapacity ,用 elementData 接收
this.elementData = new Object[initialCapacity];
// Vector需要自动扩容时增加的容量值,不传递时默认为0,在后面的扩容方法中会体现出来
this.capacityIncrement = capacityIncrement;
}
  • 变量说明:
// 存放集合的元素值,类型Object类型,可以存储任意类型的数据
protected Object[] elementData; // 数组需要进行扩容的的增量值,只有在调用有两个参数的构造函数是才可以改变其大小
// 如果采用其他两个构造函数创建集合,则默认是0
protected int capacityIncrement;
  • Vector父类的构造函数
/**
* Vector父类的构造函数,不做任何事情
*/
protected AbstractList() {}

4. 有参构造(传入一个Collection类型的集合)

/**
* 在初始化的时候直接将一个集合传入,可以把传入集合的元素全部复制到创建的新集合中
* @param c 传入的集合
* @throws NullPointerException 当传入的集合为空时,会抛出异常
*/
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

2. 添加一个元素的过程以及扩容机制

1. 调用对应类型的装箱方法

​ 在每次添加数据时,如果数据是基本数据类型,会先将基本数据类型进行装箱操作,把基本数据类型转换成对应的包装类型(引用数据类型)

// 例如:集合中存放Integer数据类型,在进行add操作时,会先进行装箱操作

/**
* 将基本数据类转换为引用数据类型
* @param i 传入的参数为一个基本型数据类型
* @return 返回的参数是一个基本数据类型的包装类(引用数据类型)
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

2. 调用 add() 方法

/**
* 添加元素方法,成功返回true,失败会抛出异常
* @param e 需要进行添加的元素值
* @return <tt>true</tt> 添加成功返回true,添加过程中如果失败会抛出异常
*/
public synchronized boolean add(E e) {
// 记录集合被修改的次数
modCount++;
// 判断是否需要扩容
ensureCapacityHelper(elementCount + 1);
// 将传递进来的元素添加到数组的末尾
elementData[elementCount++] = e;
return true;
}
  • 变量说明:
// 记录集合被修改的次数
protected transient int modCount = 0; // 记录当前的vector集合中存储的实际元素个数
protected int elementCount;

3. 判断是否需要扩容 ensureCapacityHelper()

/**
* 按照传递进来的具体值,判断是否需要进行扩容操作
* @param minCapacity 在原来集合实际元素个数的数目上加1,代表添加元素之后数组的实际长度
*/
private void ensureCapacityHelper(int minCapacity) {
// 当实际需要的数组长度 - 数组实际的长度大于0时,表示需要进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity); // 扩容的核心方法
}

4. 核心扩容方法 grow()

/**
* 具体的扩容方法
* @param minCapacity 经过判断之后存储数据的数组需要的容量大小
*/
private void grow(int minCapacity) {
// 获取未扩容之前的数组长度
int oldCapacity = elementData.length;
// 判断 capacityIncrement 增量值是否大于0,如果大于0每次扩容大小是增量值,否则按原来长度的2倍扩容
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); // 当扩容之后的长度小于实际需要的长度时,将实际需要的长度作为扩容之后的长度
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // 当扩容之后的长度大于限定的最大长度时
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity); // 使用数组的 copyOf 对数组进行扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
  • 变量说明:
// 整数类型的最大值减8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

5. 当数组要求的扩容的长度超过限定的最大值时

/**
* 具体的扩容方法
* @param minCapacity 经过判断之后存储数据的数组需要的容量大小
*/
private static int hugeCapacity(int minCapacity) {
// 判断是否是内存溢出
if (minCapacity < 0)
throw new OutOfMemoryError(); // 判断实际需要的数组长度是否大于设置的最大值,是则返回整数类型的最大值,否则返回设置的最大值
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

Vector总结及部分底层源码分析的更多相关文章

  1. List-LinkedList、set集合基础增强底层源码分析

    List-LinkedList 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 继上一章继续讲解,上章内容: List-ArreyLlist集合基础增强底层源码分析:https:// ...

  2. List-ArrayList集合基础增强底层源码分析

    List集合基础增强底层源码分析 作者:Stanley 罗昊 [转载请注明出处和署名,谢谢!] 集合分为三个系列,分别为:List.set.map List系列 特点:元素有序可重复 有序指的是元素的 ...

  3. LInkedList总结及部分底层源码分析

    LInkedList总结及部分底层源码分析 1. LinkedList的实现与继承关系 继承:AbstractSequentialList 抽象类 实现:List 接口 实现:Deque 接口 实现: ...

  4. JAVA ArrayList集合底层源码分析

    目录 ArrayList集合 一.ArrayList的注意事项 二. ArrayList 的底层操作机制源码分析(重点,难点.) 1.JDK8.0 2.JDK11.0 ArrayList集合 一.Ar ...

  5. 分布式缓存技术之Redis_Redis集群连接及底层源码分析

    目录 1. Jedis 单点连接 2. Jedis 基于sentinel连接 基本使用 源码分析 本次源码分析基于: jedis-3.0.1 1. Jedis 单点连接   当是单点服务时,Java ...

  6. Servlet和Tomcat底层源码分析

    Servlet 源码分析   Servlet 结构图 Servlet 和 ServletConfig 都是顶层接口,而 GenericServlet 实现了这两个顶层接口,然后HttpServlet ...

  7. 持久层Mybatis3底层源码分析,原理解析

    Mybatis-持久层的框架,功能是非常强大的,对于移动互联网的高并发 和 高性能是非常有利的,相对于Hibernate全自动的ORM框架,Mybatis简单,易于学习,sql编写在xml文件中,和代 ...

  8. Java——HashMap底层源码分析

    1.简介 HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的. HashMap 最多只允许一条记录的key为 nu ...

  9. Java——ArrayList底层源码分析

    1.简介 ArrayList 是最常用的 List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问.数组的缺点是每个元素之间不能有间隔, 当数组大小不满足时需要增加存储能力,就要将已经有数 ...

随机推荐

  1. 单片机STM32开发中常用库函数分析

    1.GPIO初始化函数 用法: voidGPIO_Configuration(void) { GPIO_InitTypeDefGPIO_InitStructure;//GPIO状态恢复默认参数 GPI ...

  2. 21.7.1 test

    \(NOI\) 模拟赛 呜呜呜 \(\cdots\cdots\) \(T1\) 类似哈夫曼编码,虽然没学过但是我依然画出了二叉树,然后尝试树形dp,并且最后还抓住了一个优化!让我兴奋地以为自己能赛时A ...

  3. 洛谷 P2252 [SHOI2002]取石子游戏|【模板】威佐夫博弈

    链接: P2252 [SHOI2002]取石子游戏|[模板]威佐夫博弈 前言: 第一眼大水题,第二眼努力思考,第 N 眼我是大水逼. 题意: 不看题目标题都应该能看出来是取石子类的博弈论. 有两堆石子 ...

  4. Linkerd 2:5 分种厘清 Service Mesh 相关术语

    API Gateway(API 网关) API gateway 位于应用程序的前面,旨在解决身份验证和授权.速率限制以及为外部消费者提供公共访问点等业务问题. 相比之下,service mesh 专注 ...

  5. IM服务器:我的千万级在线聊天服务器集群

    一.服务器特点 01.傻瓜式部署,一键式启动: 02.单机支持10万以上在线用户聊天(8G内存,如果内存足够大,并发量可超过10万): 03.支持服务器集群,集群间高内聚.低耦合,可动态横向扩展IM服 ...

  6. 如何使用python 新建文件夹以及递归创建文件夹

    转载:如何使用python 新建文件夹以及递归创建文件夹 | 酷python (coolpython.net) 1. os.mkdir 使用python创建文件夹,通常使用os.mkdir方法,在使用 ...

  7. linux中解压.tgz, .tar.gz ,zip ,gz, .tar文件

    转载:https://blog.csdn.net/fu6543210/article/details/7984578 将.tgz文件解压在当前目录: tar zxvf MY_NAME.tgz 将.ta ...

  8. Spring事务不生效问题

    事务未生效可能造成严重的数据不一致性问题,因而保证事务生效至关重要.Spring事务是通过Spring aop实现的,所以不生效的本质问题是spring aop没生效,或者说没有代理成功,所以有必要了 ...

  9. TypeScript 泛型及应用

    TypeScript 泛型及应用 一.泛型是什么 二.泛型接口 三.泛型类 四.泛型约束 4.1 确保属性存在 4.2 检查对象上的键是否存在 五.泛型参数默认类型 六.泛型条件类型 七.泛型工具类型 ...

  10. js--数组的 Array.of() 和 Array.from() 方法的使用总结

    前言 JavaScript 中数组的本质是一个对象,它存在的 length 属性值随数组元素的长度变化,但是开发中经常会遇到拥有 length 属性和若干索引属性的对象,被称为类数组对象,类数组对象和 ...