详解Vector

2023-10-26

目录

一、Vector介绍

二、源码解析

1、Vector实现的接口

2、Vector的构造方法

(1)无参构造方法

(2)带初始容量的构造方法

 (3)带初始容量和增量的构造方法

(4)集合型构造方法

3、Vector中的变量

4、Vector主要方法解析

(1)add方法解析

(2)remove()方法

(3)set()方法

(4) get()方法

(5)其他方法

三、总结

1、Vector简单总结

2、对比ArrayList


一、Vector介绍

        Vector是实现了List接口的子类,其底层是一个对象数组,维护了一个elementData数组,是线程安全的,Vector类的方法带有synchronized关键字,在开发中考虑线程安全中使用Vector。

二、源码解析

1、Vector实现的接口

        如下图所示:

         观察上图,发现Vector继承了AbstractList<E>,并实现了三个接口,分别是:Serializable、Cloneable和RandomAccess。下面对Vector中继承的类和实现的接口进行简单的介绍:

  • AbstractList类:该类实现了List接口里面的方法,并且为其提供了默认代码实现。而List接口中主要定义了集合常用的方法让ArrayList进行实现,如:add、addAll、contains、remove、size、indexOf等方法。
  • Serializable接口:主要用于序列化,即:能够将对象写入磁盘。与之对应的还有反序列化操作,就是将对象从磁盘中读取出来。因此如果要进行序列化和反序列化,ArrayList的实例对象就必须实现这个接口,否则在实例化的时候程序会报错(java.io.NotSerializableException)。
  • Cloneable接口:实现Cloneable接口的类能够调用clone方法,如果没有实现Cloneable接口就调用方法,就会抛出异常(java.lang.CloneNotSupportedException)。
  • RandomAccess接口:该接口表示可以随机访问ArrayList当中的数据。随机访问是指我们可以在常量时间复杂度内进行数据的方法,因为ArrayList的底层实现是数组,而数组是可以随机访问的。

2、Vector的构造方法

(1)无参构造方法

    public Vector() {
        this(10);
    }

         总结:Vector的无参构造方法中会调用有参构造方法,创建一个内部数组,该内部数组的初始容量为10,增量为0

(2)带初始容量的构造方法

    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

        总结:构造一个内部数组,该数组的容量为指定的容量,增量为0。

 (3)带初始容量和增量的构造方法

    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        //如果初始容量小于0,抛出异常
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        //指定容量
        this.elementData = new Object[initialCapacity];
        //指定增量
        this.capacityIncrement = capacityIncrement;
    }

        总结:构造一个具有初始容量和增量的数组。 

(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)
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }

总结:构造指定集合元素的数组。 

3、Vector中的变量

        Vector底层也是数组实现的,其主要变量有:

  • elementData::Vector是基于数组的一个实现,elementData就是底层的数组
  • elementCount:数组元素个数
  • capacityIncrement:指定Vector容量不足的扩容量,不指定的情况下默认翻倍

4、Vector主要方法解析

(1)add方法解析

        从上面对于Vector构造方法的分析,不难发现Vector和ArrayList的默认初始容量都是10。那么,我们看看Vector的add()方法又是如何实现的?

    public synchronized boolean add(E e) {
        //AbstractList中的变量
        modCount++;
        //确保数组容量是否足够
        ensureCapacityHelper(elementCount + 1);
        //把元素添加到数组中
        elementData[elementCount++] = e;
        return true;
    }

        可以看到:add方法添加一个元素到列表的末尾。它首先通过ensureCapacityHelper(elemetnCount+1)来保证Object[]数组有足够的空间存放添加的数据,然后再将添加的数据存放到数组对应位置上。 

    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

         通过ensureCapacityHelper()方法判断最小容量和当前数组长度,若所需的最小容量大于数组大小,则需要进行扩容,然后调用grow()方法实现扩容。

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private void grow(int minCapacity) {
        //旧数组的长度
        int oldCapacity = elementData.length;
        //如果指定了增量,则新数组长度=旧数组长度+增量;如果没有指定容量,则新数组长度=旧数组长度2倍
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        //判断新容量减去最小容量是否小于0,如果是第一次调用add,则必然小于
        if (newCapacity - minCapacity < 0)
            //将最小容量赋给新容量
            newCapacity = minCapacity;
        /判断新容量减去最大数组大小是否大于0,如果时则计算出一个超大容量
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //调用数组工具类方法,创建一个新数组,将新数组的地址赋值给elementData
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    private static int hugeCapacity(int minCapacity) {
        //如果最小容量小于0,抛出异常;否则就比较并返回
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

Step1:先将当前数组大小赋值给oldCapacity,然后判断是否有指定增量的值,如果有,则新数组长度=旧数组长度+增量;如果没有,则新数组长度=旧数组长度*2。

Step2:利用newCapacity进行两次判断:

  • 第一次判断 if (newCapacity - minCapacity < 0),判断扩容后容量是否大于minCapacity,若小于minCapacity,则直接将minCapacity赋值给newCapacity
  • 第二次判断 if (newCapacity - MAX_ARRAY_SIZE > 0),判断newCapacity 是否超出了ArrayList所定义的最大容量,若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE, 如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为 Interger.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。

Step3:最终得到newCapacity,然后调用Arrays.copyOf()方法进行扩容

        综合上述分析,有以下几个点:

  • Vector如果不指定初始容量,则默认创建一个长度为10的数组。
  • Vector如果不指定初始增量,则扩容机制为:新数组长度=旧数组长度*2;如果指定初始增量,则扩容机制为:新数组长度=旧数组长度+增量。
  • Vector的add()方法是加了synchronized关键字的,这就意味着它是线程安全的。

        接下来可以看看如何在指定位置添加元素:

    //在指定位置添加元素
    public void add(int index, E element) {
        insertElementAt(element, index);
    }
    //添加元素到指定位置
    public synchronized void insertElementAt(E obj, int index) {
        modCount++;
        //检查位置合法性
        if (index > elementCount) {
            throw new ArrayIndexOutOfBoundsException(index
                                                     + " > " + elementCount);
        }
        //判断是否需要扩容
        ensureCapacityHelper(elementCount + 1);
        //拷贝数组
        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
        //添加元素
        elementData[index] = obj;
        elementCount++;
    }

        可以看到:在指定位置添加元素,首先进行了数组范围的检查,防止越界,然后调用方法检验是否要扩容,且增量++,之后完成数组拷贝即可。 

(2)remove()方法

    public boolean remove(Object o) {
        return removeElement(o);
    }
    public synchronized boolean removeElement(Object obj) {
        modCount++;
        //获取该元素所在的数组下标
        int i = indexOf(obj);
        //如果该元素存在,则移除元素
        if (i >= 0) {
            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) {        //下标小于0,抛出异常
            throw new ArrayIndexOutOfBoundsException(index);
        }
        //删除元素
        int j = elementCount - index - 1;
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }

         由上述分析:删除元素同样需要进行范围校验。然后计算删除需要移动的数据,再通过数组拷贝移动数组。其次还有一个小细节,可以发现remove()方法是有返回值的,而这个返回值就是我们删除的元素的值。 同样的,真正移除元素的remove()方法也是加锁了的。

(3)set()方法

        该方法加了synchronized关键字保证安全性,用来设置指定下标的数据,进行元素数据的更新。

    public synchronized E set(int index, E element) {
        //判断合法性
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        //修改旧值
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

(4) get()方法

 该方法用来获取对应下标的数据,也是加锁的方法。

    public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }

(5)其他方法

        Vector中的其他方法,如:判断是否为空、获取长度等方法,都是加了锁的。因此,可以认为Vector是线程安全的。

 

         关于Vector中的迭代器,这里不再赘述,有兴趣的可以看这篇文章,里面对Itr进行了介绍:深入理解ArrayList

三、总结

1、Vector简单总结

  • Vector的底层实现也是数组,在不指定初始容量的情况下,默认初始数组大小为10,其扩容机制为:当指定了增量的时候,新扩容的容量=旧数组长度+容量;如果没有指定增量,新扩容容量=旧数组长度*2。
  • Vector是线程安全的,因为它对很多方法都加锁了。
  • Vector和ArrayList都是数组实现的,因此其支持快速随机访问,但增加元素和删除元素的操作却是比较耗时的。

2、对比ArrayList

相同:两个类都实现了List接口,它们都是有序且元素可重复的集合。

不同:

(1)ArrayList 是线程不安全的,Vector 是线程安全的。ArrayList 是线程不安全的,所以当我们不需要保证线程安全性的时候推荐使用 ArrayList,如果想要在多线程中使用 ArrayList 可以通过 Collections.synchronizedList(new ArrayList()) 或 new CopyOnWriteArrayList 的方式创建一个线程安全的 ArrayList 集合;Vector 类的所有方法都是同步的。可以有两个线程安全的访问一个 Vector 对象,但是一个线程访问 Vector 的话会在同步操作上耗费大量的时间。

(2)ArrayList 使用默认构造器创建对象时是在调用 add() 方法时对 ArrayList 的默认容量进行初始化的,Vector 在调用构造器时就对容量进行了初始化

(3)ArrayList 存储数据的 Object 数组使用了transient关键字,Vector 的 Object 数组没有。

        关于transient关键字的说明:如果用 transient 声明一个实例变量,当对象存储时,它的值不需要维持。这里的对象存储是指,Java 的 serialization 提供的一种持久化对象实例的机制。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。例如:当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization 机制来保存它。为了在一个特定对象的一个域上关闭 serialization,可以在这个域前加上关键字 transient。

        简单的说,就是被 transient 修饰的成员变量,在序列化的时候其值会被忽略,在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null

(4)扩容机制不同。ArrayList扩容机制为变为原来的1.5倍,而Vector扩容时如果指定了增量,则新数组长度=旧数组长度+增量,如果没有指定,就扩容为原来的2倍。 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

详解Vector 的相关文章

  • Netbeans 8.1 Gnome 3 GTK+ UI 字体和选项卡高度

    我刚刚在运行 GNOME 3 桌面的 Ubuntu 16 04 上安装了 NetBeans 8 1 如果可能的话 我想继续使用 IDE 的 GTK 外观和感觉 但 UI 上的字体 尤其是选项卡中的字体 太小且重叠 我尝试添加 fontsiz
  • 带路径压缩算法的加权 Quick-Union

    有一种 带路径压缩的加权快速联合 算法 代码 public class WeightedQU private int id private int iz public WeightedQU int N id new int N iz new
  • Java:扩展类并实现具有相同方法的接口

    可能无法完成以下操作 我收到编译错误 继承的方法 A doSomthing int 无法隐藏 B 中的公共抽象方法 public class A int doSomthing int x return x public interface
  • TreeMap 删除所有大于某个键的键

    在项目中 我需要删除键值大于某个键的所有对象 键类型为Date 如果重要的话 据我所知TreeMapJava中实现的是红黑树 它是一种二叉搜索树 所以我应该得到O n 删除子树时 但除了制作尾部视图并一一删除之外 我找不到任何方法可以做到这
  • eclipse行号状态行贡献项是如何实现的?

    我需要更新状态行编辑器特定的信息 我已经有了自己的实现 但我想看看 eclipse 贡献项是如何实现的 它显示状态行中的行号 列位置 谁能指点一下 哪里可以找到源代码 提前致谢 亚历克斯 G 我一直在研究它 它非常复杂 我不确定我是否了解完
  • Android:文本淡入和淡出

    我已阅读此 stackoverflow 问题和答案 并尝试实现文本淡入和淡出 Android中如何让文字淡入淡出 https stackoverflow com questions 8627211 how to make text fade
  • 为什么即使我的哈希码值相同,“==”也会返回 false

    我写了一个像这样的课程 public class HashCodeImpl public int hashCode return 1 public static void main String args TODO Auto generat
  • 什么是抽象类? [复制]

    这个问题在这里已经有答案了 当我了解抽象类时 我说 WT H 问题 创建一个无法实例化的类有什么意义呢 为什么有人想要这样的课程 什么情况下需要抽象类 如果你明白我的意思 最常见的是用作基类或接口 某些语言有单独的interface构建 有
  • Mockito 使用 @Mock 时将 Null 值注入到 Spring bean 中?

    由于我是 Spring Test MVC 的新手 我不明白这个问题 我从以下代码中获取了http markchensblog blogspot in search label Spring http markchensblog blogsp
  • 在 Java 中如何找出哪个对象打开了文件?

    我需要找出答案哪个对象在我的 Java 应用程序中打开了一个文件 这是为了调试 因此欢迎使用工具或实用程序 如果发现哪个对象太具体了 这class也会很有帮助 这可能很棘手 您可以从使用分析器开始 例如VisualVM http visua
  • 如何在.NET中使用java.util.zip.Deflater解压缩放气流?

    之后我有一个转储java util zip Deflater 可以确认它是有效的 因为 Java 的Inflater打开它很好 并且需要在 NET中打开它 byte content ReadSample sampleName var inp
  • 在 Java 中获取并存储子进程的输出

    我正在做一些需要我开始子处理 命令提示符 并在其上执行一些命令的事情 我需要从子进程获取输出并将其存储在文件或字符串中 这是我到目前为止所做的 但它不起作用 public static void main String args try R
  • 不可变的最终变量应该始终是静态的吗? [复制]

    这个问题在这里已经有答案了 在java中 如果一个变量是不可变的并且是final的 那么它应该是一个静态类变量吗 我问这个问题是因为每次类的实例使用它时创建一个新对象似乎很浪费 因为无论如何它总是相同的 Example 每次调用方法时都会创
  • 如何区分从 Saxon XPathSelector 返回的属性节点和元素节点

    给定 XML
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • Android S8+ 警告消息“不支持当前的显示尺寸设置,可能会出现意外行为”

    我在 Samsung S8 Android 7 中收到此警告消息 APP NAME 不支持当前的显示尺寸设置 可能会 行为出乎意料 它意味着什么以及如何删除它 谢谢 通过添加解决supports screens 机器人 xlargeScre
  • java XMLSerializer 避免复杂的空元素

    我有这个代码 DocumentBuilderFactory factory DocumentBuilderFactory newInstance DocumentBuilder builder factory newDocumentBuil
  • Android View Canvas onDraw 未执行

    我目前正在开发一个自定义视图 它在画布上绘制一些图块 这些图块是从多个文件加载的 并将在需要时加载 它们将由 AsyncTask 加载 如果它们已经加载 它们只会被绘制在画布上 这工作正常 如果加载了这些图片 AsyncTask 就会触发v
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • java'assert'和'if(){}else exit;'之间的区别

    java和java有什么区别assert and if else exit 我可以用吗if else exit代替assert 也许有点谷歌 您应该记住的主要事情是 if else 语句应该用于程序流程控制 而assert 关键字应该仅用于

随机推荐

  • ThreadPoolExecutor问题定位

    今天定位了一个内存泄露的问题 错误如下 Exception in thread http exec 24 java lang OutOfMemoryError unable to create newnative thread 众所周知 内
  • 应用程序无法正常启动(0x000007b)或者找不到dll文件(以vcruntime140d.dll为例)的原因原理分析和解决方法(亲测已解决)

    文章目录 一 问题1 由于找不到vcruntime140d dll 无法继续执行代码 重新安装程序可能会解决此问题 二 问题2 应用程序无法正常启动 0x000007b 请单击 确定 关闭应用程序 三 Windows目录下的SysWOW64
  • 【开始学习React Hook(1)】Hook之useState

    react hook是react推出的一种特殊函数 这些函数可以让你在不创建react class的情况下依然可以使用react的一些特性 诸如目前react的钩子函数拥有的所有特性 最常用的hook有useState useEffect
  • 网络安全——从新手入门到大师的学习攻略

    最近经常有网友问我网络安全方面怎么样从入门到大师 或者成为黑客 红客等 其实我想说的是 只要你有毅力 能坚持学习各种必须的网络安全技术和理论知识 随着时间的积累 最终一定会成为你心中最想成为的网络安全专家 本文就着重讲解下网络安全从入门到大
  • Docker搭建漏洞靶场(Vulhub、Vulnapp、Vulfocus)

    文章目录 vulhub 靶场搭建 简介 环境搭建过程 vulnapp靶场搭建 vulfocus靶场搭建 简介 环境搭建 vulhub 靶场搭建 简介 Vulhub是一个面向大众的开源漏洞靶场 无需docker知识 简单执行一条命令即可编译
  • 数据中心联盟第五批大数据产品评测结果出炉,腾讯云大数据斩获多个奖项

    欢迎大家前往腾讯云社区 获取更多腾讯海量技术实践干货哦 近日 在数据中心联盟组织的第五批大数据产品评测中 腾讯云大数据平台取得了两项第一名 特别在Hbase性能上有非常亮眼的表现 其他各项成绩也名列前茅 本月7日 中国通信标准化协会常务副秘
  • 以太坊学习-笔记1

    加密 以太坊有两种不同类型的账户 外部拥有账户 EOA 和合约 EOA 的以太坊地址是从密钥对的公钥部分生成的 以太坊使用的系统是基于公钥加密的系统 密钥是成对出现的 由一个私有 秘密 密钥和一个公共密钥组成 私钥提供对账户的控制权 公钥将
  • Java加密之IV

    AES是一种 分组密码 密码学中 分组 block 密码的工作模式 mode of operation 允许使用同一个分组密码密钥对多于一块的数据进行加密 并保证其安全性 分组密码自身只能加密长度等于密码分组长度的单块数据 若要加密变长数据
  • LeetCode 127. 单词接龙(C++)*

    思路 1 如果采用回溯法来的话会超时 2 这里采用构造图和广度优先遍历结合来实现 首先要构造图 需要将每个字符串对应一个数字id 然后边的构造使用矩阵来实现 这里采用将每一个字符串的id连接每个将该字符串的其中一个字符改为未知字符的字符串的
  • Python ffmpeg视频处理

    2 源码 coding utf 8 import ffmpeg import getpass import subprocess import matplotlib pyplot as plt import cv2 import numpy
  • android studio(自带SDK)安装教程

    下载网址 http www android studio org index php download hisversion 当下载为 exe程序时 直接双击 exe程序 之后点击Next 第一地址是安装路径 自己选择即可 第二个是SDK路
  • 手把手教你搭建Windows环境微信小程序的本地测试服务器

    Mac环境下 手把手教你搭建Mac环境微信小程序的本地测试服务器 问题的提出 Windows环境 方便快捷地搭建小程序的测试服务器 小程序对于网络请求的URL的特殊要求 不能出现端口号 不能用localhost 必须用https 主要步骤
  • elementUI侧边栏实现响应式,响应式侧边栏

    实现思路 准备两份aside侧边栏 借助display和媒体查询实现响应式 以下是完整代码
  • 初次使用vite新建项目报错

    第一次使用vite新建项目时报错 C Users AppData Roaming npm cache npx 13100 node modules create vite index js 4 import fs from node fs
  • 测试报告(进阶)

    功能测试可以手写一份测试报告 一 如何自动生成测试报告 unittest生成测试报告 测试用例 账号正确 密码错误 自己依照测试用例输入一组账号 点登录 会出现信息 密码错误 代码 import unittest import time f
  • vue 监听div宽高变化

    npm install element resize detector save import elementResizeDetectorMaker from element resize detector mounted const th
  • 如何使用chatglm-6b实现多卡训练

    首先先说ChatGLM 6b是支持多卡训练的 步骤如下 1 安装 NVIDIA CUDA Toolkit 要使用多卡训练 需要安装 CUDA Toolkit 可以在 NVIDIA 官网下载适用于操作系统的 CUDA 版本 2 确认所有的显卡
  • 实验四 MGRE与OSPF综合实验

    1 R6为ISP只能配置IP地址 R1 R5的环回为私有网段 2 R1 4 5为全连的MGRE结构 R1 2 3的星型的拓扑结构 R1为中心站点 3 所有私有网段可以互相通讯 私有网段使用OSPF完成 新建拓扑图 配置合理的IP R1 R2
  • 解决IDEA、PyCharm、PhpStorm及Android Studio中输入法卡住、光标不跟随的问题

    2017新版JetBrains全家桶下的各个软件都存在使用中文输入法时出现类似卡住 即光标不跟随的现象 解决办法 删除软件所在根目录下的jre或jre64文件夹 删除后软件会自动使用本机的jre 并可能提示jie已不是最新版 但不影响使用
  • 详解Vector

    目录 一 Vector介绍 二 源码解析 1 Vector实现的接口 2 Vector的构造方法 1 无参构造方法 2 带初始容量的构造方法 3 带初始容量和增量的构造方法 4 集合型构造方法 3 Vector中的变量 4 Vector主要