Vector源码解析

news2024/11/28 6:28:34

Vector源码解析

简介

Vector 是一个古老的 线程安全(内部的核心方法都加了synchronized) 的容器,在 JDK1.0 时就已经存在,到如今已经很少使用。

基本结构与 ArrayList 类似,可以认为是线程安全版本的 ArrayList,但因为 Vector 效率低下,所以在多线程下使用的基本都是 CopyOnWriteArrayList,其效率更高。

Vector 实现了 List 接口,是顺序容器,即元素存存放的数据与放进去的顺序相同,允许存入 null 元素。底层通过数组实现。

继承体系

image-20221127160615501

Vector 继承了 AbstractList,实现了 List,RandomAccess,Cloneable,Serializable 接口,因此 Vector 支持快速随机访问,可以被克隆,支持序列化。

源码解析

属性

    // 底层存储数据的数组
	// 注意:访问修饰符有所不同,Vector用protected修饰,而ArrayList用private修饰。
	// JavaSE中:private变量只能被当前类的方法访问,而protected可以被同一包中的所有类和其他包的子类访问
	protected Object[] elementData;
	
    // 数组中的元素个数
    protected int elementCount;
	
    /*
     * 扩容时指定扩容的容量(>0),即扩容后的容量为 oldCap + capacityIncrement
     * 若没有指定,那么默认扩容为原容量的2倍,即扩容后的容量为 2 * oldCap
     */
    protected int capacityIncrement;

构造方法

  • 无参和单参都是调用的是双参构造方法,默认初始化容量为 10
// ----------------------------构造器1----------------------------------
	/*
     * 传入初始容量和扩容数量
     * 这里跟ArrayList不同的是,Vector是非懒加载的,调用构造器就会初始化数组
     */
	public Vector(int initialCapacity, int capacityIncrement) {	
        // 判断初始容量是否合法
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        // 直接构造数组赋值给内部的elementData
        this.elementData = new Object[initialCapacity];
        // 传入的扩容数量赋值给capacityIncrement
        this.capacityIncrement = capacityIncrement;
    }
// ----------------------------构造器2-----------------------------------
	/*
	 * 调用是构造器1,只是扩容数量传入的是0,即扩容时默认扩容为原长度的2倍。
	 */
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

// ----------------------------构造器3-----------------------------------
	/*
	 * 底层调用构造器2. 默认初始化长度为10. capacityIncrement为0
	 */
    public Vector() {
        this(10);
    }
// ----------------------------构造器4-----------------------------------
	/*
	 * 参数为集合类型的构造函数
	 */
    public Vector(Collection<? extends E> c) {
        elementData = c.toArray();
        elementCount = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            // 将参数集合c 中的数据拷贝到elementData
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }

add(E e)

  • 添加元素至数组末尾。
  	/*
  	 * 加锁方法,线程安全
  	 */
	public synchronized boolean add(E e) {
        modCount++;
        add(e, elementData, elementCount);
        return true;
    }
              	 ||    
  		    	 ||
 				 \/	
	/*
	 * @param e 添加的元素
	 * @param elementData 内部数组
	   @param s 元素个数
	 */
    private void add(E e, Object[] elementData, int s) {
        // 判断元素个数等于数组长度,下面是扩容的逻辑
        if (s == elementData.length)
            elementData = grow();
        // s就是待插入的位置,将e放到集合中
        elementData[s] = e;
        // 元素个数+1
        elementCount = s + 1;
    }

grow()

  • 扩容,拷贝出一个新数组。
    /*
     * 调用了重载的带参数的grow(int minCapacity)方法
     */
	private Object[] grow() {
        // 传入elementCount + 1
        return grow(elementCount + 1);
    }	
              	 ||    
  		   		 ||
 				 \/
	/*
	 * 1.首先调用newCapacity()方法,计算出扩容后数组的容量x
	 * 2.然后调用Arrays.copyOf()方法重新创建一个长度为x的数组,然后将原数组中的数据拷贝过去。完成扩容操作
	 *
	 * @param minCapacity:当前数组需要存放的元素的个数(即所需要的长度)
	 */
	private Object[] grow(int minCapacity) {
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }

newCapacity(int minCapacity)

  • 真正计算扩容后数组长度的方法。
    /*
     * 计算出最终扩容后的数组长度
     */
	private int newCapacity(int minCapacity) {
        // 获取当前数组长度
        int oldCapacity = elementData.length;
        /*
         * 这里就是计算扩容后的长度的核心。
         * 1.首先判断capacityIncrement是否大于0,如果大于0,那么这次扩容的长度为oldCapacity + capacityIncrement
         * 2.否则,扩容后的长度为 oldCapacity * 2
         */
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        // 判断按照上面的计算后是否满足当前所需要的长度,不满足的话进入if语句
        if (newCapacity - minCapacity <= 0) {
            // 溢出 直接抛错
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            // 没有溢出,直接将当前所需要的长度返回
            return minCapacity;
        }
        /*
         * MAX_ARRAY_SIZE = INF - 8
         * 这里去判断newCapacity是否溢出或者newCapacity大于MAX_ARRAY_SIZE
         * 并且没有溢出,此时直接分配INF。
         * 这里的处理ArrayList相同,参考[https://blog.csdn.net/qq_46312987/article/details/122096262]
         */
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }
	                ||    
  		    	    ||
 				    \/
    private static int hugeCapacity(int minCapacity) {
        // 溢出 直接抛OOM
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        
        return (minCapacity > MAX_ARRAY_SIZE) ?
            // minCapacity > MAX_ARRAY_SIZE 并且没有溢出(上面if判断),直接分配INF
            Integer.MAX_VALUE :
        	// 否则分配MAX_ARRAY_SIZE
            MAX_ARRAY_SIZE;
    }

set(int index, E element)

  • 修改指定索引位置的元素。
    /*
     * (同步方法)修改指定索引位置的元素
     */
	public synchronized E set(int index, E element) {
        // 判断索引是否越界
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
		// 获取index位置的元素
        E oldValue = elementData(index);
        // 替换
        elementData[index] = element;
        // 返回原元素
        return oldValue;
    }

get(int index)

  • 获取指定索引位置的元素。
   /*
    * (同步方法)获取指定索引位置的元素
    */
	public synchronized E get(int index) {
		// 索引越界判断
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
		// 直接返回index位置的元素。
        return elementData(index);
    }
                ||    
  		    	||
 				\/
	/*
	 * 获取指定索引位置的元素
	 */
    E elementData(int index) {
        return (E) elementData[index];
    }

remove(int index)

  • 删除指定索引位置的元素。
    /*
     * (同步方法)将指定索引位置的元素置为null
     */
	public synchronized E remove(int index) {
        modCount++;
        // 越界判断
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        // 获取index位置的元素
        E oldValue = elementData(index);
        // 这里计算出当前准备删除的元素后有多少个元素
        int numMoved = elementCount - index - 1;
        // 调用System.arraycopy变向的将index位置后面的所有元素往前移动一位
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        // 将最后一个位置置为null
        // 获取完下标后 elementCount值自减1
        elementData[--elementCount] = null; // Let gc do its work
        // 返回旧的value
        return oldValue;
    }
                ||    
  		    	||
 				\/
	/*
	 * 获取指定索引位置的元素
	 */
    E elementData(int index) {
        return (E) elementData[index];
    }

其它方法

    // 设置Vector数组的大小
    public synchronized void setSize(int newSize) {
        // 修改次数++
        modCount++;
        // 判断设置的数组大小是否大于Vector中有存储的效元素的个数
        // 若 newSize > Vector中有存储的效元素的个数,则调整Vector的大小
        if (newSize > elementCount) {
            // 调用判断是否扩容的方法,如果需要扩容则该方法内部调用扩容方法grow()
            ensureCapacityHelper(newSize);
        } else {
            // 如果上述判断不成立,则将newSize位置之后开始的元素都设置为null
            for (int i = newSize ; i < elementCount ; i++) {
                elementData[i] = null;
            }
        }
        // 更新有效元素个数
        elementCount = newSize;
    }

    // 获取Vector的当前容量
    public synchronized int capacity() {
        return elementData.length;
    }

	// 判断elementCount是否为0
    public synchronized boolean isEmpty() {
        return elementCount == 0;
    }

    // 获取Vector里面的有效元素个数
    public synchronized int size() {
        return elementCount;
    }

    // 判断Vecotor中是否包含元素 o
    public boolean contains(Object o) {
        return indexOf(o, 0) >= 0;
    }

    // 获取Vector数组中第一次出现对象o的下标,如果不存在,那么返回-1
    public int indexOf(Object o) {
        return indexOf(o, 0);
    }

    // 返回从index出开始第一次出现对象o的下标,如果不存在,那么返回-1
    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 是非懒加载的,调用无参构造器时默认会初始化一个长度为 10 的数组。
  • 默认的扩容为原长度的 2 倍 (ArrayList 为 1.5 倍)(未给 capacityIncrement 赋值时)。
  • Vector 的方法基本都是加了 synchronized 的,是一个 线程安全 的容器。



参考文章

  • shstart7_Vector源码解析
  • 兴趣使然的草帽路飞_JDK集合源码之Vector解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/88320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BCN-PEG-FITC 环丙烷环辛炔聚乙二醇荧光素 BCN-PEG-荧光素

双环[6,1,0]壬炔 (BCN) &#xff08;环丙烷环辛炔&#xff09;可以通过无铜的点击化学与叠氮化物标记的分子或生物分子反应生成稳定的三氮唑连接。 产品名称 BCN-PEG-FITC 环丙烷环辛炔聚乙二醇荧光素 中文名称 环丙烷环辛炔聚乙二醇荧光素 英文名称 BCN-PEG-FITC 分子…

基于Android的二维码识别系统的研究 与实现

XXXX 本科生毕业设计(论文) 学院(系)&#xff1a; XX 专 业&#xff1a; XX 学 生&#xff1a; XX 指导教师&#xff1a; XX XX 完成日期 年 月 XXX本科生毕业设计&#xff08;论文&#xff09; 基于Android的二维码识别系统的研究 与实现 Research and Implementati…

防重放、防篡改攻击的实现(Java版)

文章目录一、问题描述1、什么是重放攻击、篡改攻击&#xff1f;2、如何模拟重放攻击、篡改攻击&#xff1f;2.1、重放攻击模拟方式2.2、篡改攻击模拟方式二、解决方案三、在项目中使用1、项目简介2、项目实践一、问题描述 1、什么是重放攻击、篡改攻击&#xff1f; 1、重放攻击…

【SSM框架】为集合类型属性赋值

&#x1f353;个人主页&#xff1a;个人主页 &#x1f352;系列专栏&#xff1a;SSM框架 目录 1.为集合类型属性赋值 ①为List集合类型属性赋值 ②为Map集合类型属性赋值 2.p命名空间 3.引入外部属性文件 1.为集合类型属性赋值 ①为List集合类型属性赋值 在Clazz类中添加…

中国三大主流开源Linux操作系统社区及其产品生态

作者&#xff1a;IT圈黎俊杰 操作系统是基础软件的核心&#xff0c;是科技研发必备的底层支撑产品&#xff0c;它的核心技术至今仍然由美国为代表的西方国家所掌控着。由于以前国产操作系统产业长期处于“多、小、散”的状态&#xff0c;未能建立起完善的生态、未能研发出真正好…

python笔记(一)

文章目录字节对象与字符转换动态爬虫实施辅助工具python 部分内置方法示例&#xff1a;python虚拟环境的创建与激活执行多个.py程序获取当前系统默认浏览器名称反编译小程序PC端小程序&#xff08;wxapkg&#xff09;解密&#xff1a;反编译字节对象与字符转换 data1 babc # …

计算机系统实验-BombLab

一.实验题目及目的 1.实验题目 程序运行在linux环境中。程序运行中有6个关卡&#xff08;6个phase&#xff09;&#xff0c;每个phase需要用户在终端上输入特定的字符或者数字才能通关&#xff0c;否则将会引爆炸弹。需要通过分析汇编代码&#xff0c;使用gdb调试等方式找到正…

oracle入门-19c 安装详细教程

我们知道学习一门技术&#xff0c;第一件事情就是搭建对应的环境。本教程将详细教大家如何安装oracle。 目录一、虚拟机创建二、挂载镜像、安装及系统配置三、 软件包传输四、数据库安装五、 图形化向导建库一、虚拟机创建 处理器数量根据自己的机器配置进行调整&#xff08;4 …

Android IPC机制

本文首发于个人博客 “胖蔡叨叨叨”&#xff0c;更多相关信息可点击查看《Android IPC机制》​​​​​​​ IPC IPC(Inter Process Communication) 进程间通信&#xff0c;是指系统中运行的不同进程间的信息交互过程。Android IPC通常用于应用间、或同应用不同进程间的数据通…

3分钟读懂数据可视化如何帮助企业智慧管理,提升管理水平

随着大数据时代的到来&#xff0c;企业管理的决策越来越倚重数据指标&#xff0c;通过数据指标了解公司运作的情况&#xff0c;产品生产销售情况&#xff0c;收入与支出情况等等&#xff0c;更大的作用是对商业趋势预测提供了决策依据&#xff0c;使公司提前布局未来&#xff0…

Q-Automation:基于ATX的自动化测试管理软件

Softing Q-Automation是基于ATX的自动化测试管理软件&#xff0c;用于测试电子控制单元&#xff08;ECU&#xff09;。该软件支持诊断协议层测试和诊断功能测试&#xff0c;并且只需填写Excel表格&#xff0c;即可实现半自动化测试需求&#xff0c;从而缩短了用户的测试周期。同…

2022年终Java编程技术总结

目录 00 总体脑图 第一章、基础篇 01 计算机基础 ## 1.1 操作系统 1.1.1 基本知识 略 1.1.2 常见Linux命令 目录文件内容搜索系统类网络权限cdcp/scpvi/vimfind: find . -name xxkillnetstat -anchmodlsmvcatgrep: ps -efgrep xxlsof: lsof-i:portmkdirrmtailwhereistopp…

web前端期末大作业 ——电影主题介绍 你好,李焕英 ——html+css+javascript网页设计实例

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 精彩专栏推荐&#x1f4…

Win11电脑蓝屏怎么办?Win11电脑蓝屏的修复方法

Win11电脑蓝屏怎么办&#xff1f;电脑蓝屏是最为常见的系统故障问题之一&#xff0c;当电脑出现蓝屏时一般分为可以开机以及无法开机的情况&#xff0c;针对这两种情况&#xff0c;下面小编分别准备了对应的解决方法&#xff0c;有需要的朋友们欢迎收藏慢慢看哦。 方法一&#…

家居类小红书达人投放总结,kol执行策略

在小红书平台上&#xff0c;许多品牌方都做了达人投放&#xff0c;但结果却反响平平&#xff0c;最后才发现是达人挑选出了问题&#xff0c;而发现这个问题的代价就是错失先机&#xff0c;也耗费大量成本来试错&#xff0c;今天为大家分享一下小红书达人投放总结以及超硬干货。…

多线程安全问题原理和4种解决办法

摘要&#xff1a;多线程访问了共享的数据,会产生线程安全问题。本文分享自华为云社区《多线程安全问题原理和解决办法Synchronized和ReentrantLock使用与区别》&#xff0c;作者&#xff1a;共饮一杯无。 线程安全问题概述 卖票问题分析 单窗口卖票一个窗口(单线程)卖100张票…

大数据学习 -- 利用Java API 将文件写入HDFS

利用API写入文件写入HDFS文件&#xff08;1&#xff09;将数据直接写入HDFS文件&#xff08;2&#xff09;将本地文件写入HDFS文件写入HDFS文件 类似于HDFS Shell里的hdfs dfs -put命令在net.zwh.hdfs包里创建WriteFileOnHDFS类 &#xff08;1&#xff09;将数据直接写入HDFS…

【记录】ChatGPT|注册流程、使用技巧与应用推荐(更新至2022年12月14日)

昨天&#xff0c;2022年12月13日&#xff0c;在下午和晚上&#xff0c;ChatGPT 就开始因为请求过多而写到一半就崩溃&#xff0c;出现network error&#xff0c;可见它的关注度确实是越来越可观了。 正好最近世界杯&#xff0c;有博客活动&#xff0c;干脆也总结一下 ChatGPT 目…

大白话讲懂word2vec原理和如何使用

前言 做自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;这个领域的小伙伴们肯定对word2vec这个模型很熟悉了&#xff0c;它就是一种最为常见的文本表示的算法&#xff0c;是将文本数据转换成计算机能够运算的数字或者向量。在自然语言处理领…

【Docker】Dockerfile从概念介绍到快速上手编写案例实战

本期目录1. Dockerfile介绍1.1 Dockerfile的重要性1.2 构建三步骤2. Dockerfile构建过程2.1 Dockerfile内容基础2.2 Dockerfile执行流程2.3 总结3. Dockerfile常用保留字指令3.1 Dockerfile常用保留字一览3.2 保留字讲解4. 案例演示-自定义CentOS镜像4.1 前期准备4.2 编写Docke…