当前位置:首页 > Java技术 > 深入解析 Java集合类ArrayList与Vector的区别

深入解析 Java集合类ArrayList与Vector的区别

2022年08月05日 13:15:20Java技术4

集合类分为两个分支,Collection与Map,其中Collection接口继承了Iterator接口,继承Iterator接口的类可以使用迭代器遍历元素(即Collection接口的类都可以使用),今天我们从相同点、不同点、以及JDK源码等各个方面来深入解析下,底层使用数组实现的两个集合类:ArrayList与Vector的区别与联系

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

区别与联系:

1.ArrayList出现于jdk1.2,vector出现于1.0.两者底层的数据存储都使用的Object数组实现,因为是数组实现,所以具有查找快(因为数组的每个元素的首地址是可以得到的,数组是0序的,所以: 被访问元素的首地址=首地址+元素类型字节数*下标    ),增删慢(因为往数组中间增删元素时,会导致后面所有元素地址的改变)的特点

2.继承的类实现的接口都是一样的,都继承了AbstractList类(继承后可以使用迭代器遍历),实现了RandomAccess(标记接口,标明实现该接口的list支持快速随机访问),cloneable接口(标识接口,合法调用clone方法),serializable(序列化标识接口)

3.当两者容量不够时,都会进行对Object数组的扩容

(1)解析ArrayList扩容源码(假设从初始开始size=0,且构造方法为: new ArrayList<>();   ):

①首先调用add方法,添加元素,在add中调用ensureCapacityInternal(确保内部容量),将当前的size(实际元素数量)传输

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

②在ensureCapacityInternal中,首先将elementData数组  与  DEFAULTCAPACITY_EMPTY_ELEMENTDATA  进行比较,这里我们假设的构造方法为下图,此时两个数组相等,minCapacity等于大的值,DEFAULT_CAPACOTY的值为10(在成员变量中定义),即minCapacity=10。

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

③modcount是在ArrayList的父类AbstractList中定义的成员变量,用于记录修改次数(对当前ArrayList的修改次数),

minCapacity=10,element.length=0,所以执行grow方法。

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

④grow方法中由于初始容量为0,所以newCapatcoty=0,然后newCapacity=minCapacity等于10 (即通常说的:ArrayList的默认构造方法,会默认分配长度为10的内存空间,这里的分配不是在创建对象时分配,而是在增加第一条数据的过程中分配,这样防止了内存的浪费),然后进行Arrays.copyOf 。如果再次扩容的话,扩容到当前容量的1.5倍。

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

(2)解析Vector扩容源码

①首先调用add方法,与arraylist相同,vector也有一个继承父类的成员变量modCount来记录修改次数。

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

②如果当前实际元素数+1大于数组定义长度,执行grow方法

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

③将elementData copy到 一个新的长度数组中,完成gorw。  其中,  capacityIncrement为自定义的增长因子(此处与arrayList不同,arraylist默认增长1.5倍;vector可以自定义若不自定义,则增长2倍,若定义则新长度=之前的长度+增长因子)  MAX_ARRAY_SIZE为数组定义的最大长度,如果是负数,则抛出OutOfMemoryError异常,如果大于MAX_ARRAY_SIZE,则赋值为Int类型的最大值。  

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

 

4.构造方法略有不同。

ArrayList:

(1)ArrayList a1 = new ArrayList(int i); 指定初始化容量的构造方法

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

 

(2)ArrayList a2 = new ArrayList(); 默认构造方法,在添加第一个元素过程中初始化一个长度为10的Object数组

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

(3) ArrayList a3 = new ArrayList(Collection); 在构造方法中添加集合,本方法创建的集合的object数组长度等于实际元素个数

  深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

Vector:

(1)Vector v1 = new Vector(10,2); 指定初始长度(initialCapacity)与增长因子(capacityIncrement)注意这里的增长因子不是oldCapacity * capacityIncrement而是+,如果不指定或者指定为0,则默认扩容当前容量的两倍,这里上面提过了

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

(2)Vector v2 = new Vector(10);  通过this关键字调用上面的构造方法,自定义初始数组长度,增长因子默认为0

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

(3)Vector v3 = new Vector(); 默认构造方法,在创建对象时便分配长度为10的Object数组。(这里在创建时便分配内存,一定意义上,浪费了内存空间)

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

5.线程的安全性不同,vector是线程安全的,在vector的大多数方法都使用synchronized关键字修饰,arrayList是线程不安全的(可以通过Collections.synchronizedList()实现线程安全)

6.性能上的差别,由于vector的方法都有同步锁,在方法执行时需要加锁、解锁,所以在执行过程中效率会低于ArrayList,另外,性能上的差别还体现在底层的Object数组上

vector:深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记arrayList:深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

可以看出来,arrayList多了一个transient关键字,这个关键字的作用是防止序列化,然后在ArrayList中重写了了readObject和writeObject方法,这样是为了在传输时提高效率,我们先来看下源码:

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

可以看到,这两个方法中将elementData数组中实际存在的元素遍历出来进行传输,假设现在容量为10000但是实际有8000元素,如果将2000空的元素也进行传输势必会影响效率,所以这么做提高了效率,节省了时间。 

 

这两个方法在序列化时如何被调用的,为什么是private修饰?在传输时,ObjectInputStream与ObjectOutputStream会通过反射调用这两个方法。  private修饰时因为,在ObjectStreamClass类中,调用的是传输对象中private修饰的writeObject与readObject(这里就不深入研究了,光从找下面这个图片的源码就可以感觉到,IO流的源码比集合类可能复杂的多)

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

defaultReadObject与dafaultWriteObject的作用?

如果类中不自定义readObject与writeObject,那么类在序列化的时候会调用ObjectInputStream与ObjectOutputStream中的defaultReadObject与defaultWriteObject方法进行默认序列化,这样的话,就不会序列化transiend中的数组。但是transiend修饰的数组是必须要序列化的! 如果自定义的话,就不会调用这两个default方法,这样的话类中所有需要序列化的都要自定义,这样太麻烦了,所以在自定义的方法中先调用下他,将不是transiend的序列化,然后再自定义object数组的序列化。

  最后再说一下,这两个集合类如何在迭代时保证线程安全,这里就要提一下上面说过的在AbstractList类中有一个静态变量

modcount(我看网上一些帖子说modcount只存在于线程不安全的集合类中,其实这种说法是错误的,在vector中也使用了modcount用于保证迭代时数据安全)他用于记录一个集合类对象被修改的次数。这两个类在迭代时(调用iterator方法时),Iterator iterator = arrayList.iterator();或Iterator iterator2 = vector.iterator(); 返回的iterator对象都是类中的一个私有内部类。这个类在调用时,便初始化了一个expectedModCount=modcount,即在迭代前先用成员变量保存下modcount的值。

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

在迭代时,首先会调用checkForComodification方法,来比较modCount的值有没有被改变,如果改变则会抛出异常,这样就保证了迭代时的安全性(这里的安全性不只是保证了多线程下的安全,也保证了单线程中迭代时,如果修改数据所造成的隐患)这就是所谓fail-fast策略(快速失败策略)。

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

深入解析     Java集合类ArrayList与Vector的区别 _ JavaClub全栈架构师技术笔记

 

 

 

作者:漫步夕阳下
来源链接:https://blog.csdn.net/qq_37113604/article/details/80836025

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/17853.html

标签: Java集合Java
分享给朋友:

“深入解析 Java集合类ArrayList与Vector的区别” 的相关文章

Java空指针异常解决java.lang.NullPointerException解决心得

Java空指针异常解决java.lang.NullPointerException解决心得

今天做课设的时候运行程序报出以下错误 java.lang.NullPointerException 首先要理解的是此错误并不会在 程序中报错,只会在运行的时候报错。 是由于某个参数(集合,数组等数据)可能出现一个null值而导致后面的程序不能运行时...

两年前写的Java基础总结书

两年前写的Java基础总结书

想法衍生 两年前的我,突发奇想,把自己学的Java基础进行规范化的整理,因为自己的文档编辑能力有限,所以写的排版不是很好,参照图书排版的形式,将书籍进行整理,可以供学习Java基础的朋友参考,由于时间有限,可能也会有问题,请指出。下载地址在最后 截图如下:...

Java中四种访问修饰符的区别

在java中共有4种访问级别,按访问权限由高到低为:public(公有的)、protected(受保护的)、友好的(没有任何访问权限关键字修饰)和private(私有的)。 类型 类内部 同一个包其...

深入理解 Java 并发锁

深入理解 Java 并发锁

📦 本文以及示例源码已归档在 javacore 一、并发锁简介 确保线程安全最常见的做法是利用锁机制(Lock、sychronized)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方法或者某个代码块,那么操作必然是原子性的,线程安全的...

全面了解 Java 原子变量类

📦 本文以及示例源码已归档在 javacore 一、原子变量类简介 为何需要原子变量类 保证线程安全是 Java 并发编程必须要解决的重要问题。Java 从原子性、可见性、有序性这三大特性入手,确保多线程的数据一致性。 确保线程安全最...

Java 内存模型

Java 内存模型

📦 本文以及示例源码已归档在 javacore Java 内存模型(Java Memory Model),简称 JMM。 JVM 中试图定义一种 JMM 来屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。...

JDBC连接时所犯错误1.字符集设置不合适2.连接MySQL8.0社区版时时区不一致3..包名不能以Java.命名4.驱动被弃用

Microsoft JDBC Driver 的主页为:https://msdn.microsoft.com/en-us/data/aa937724.aspx 下载所需驱动 今天连接时报了四次错,记录下来 1.java.sql.SQLException:...

Java实现阶乘运算

n!=123*…n 学习编程就是要了解从问题到程序是如何实现的 Scanner in=new Scanner(System.in); int n ; n=in.nextInt(); // int i=1; int factor=1;...

java之整数的分解可以理解为倒序输出

Scanner in=new Scanner(System.in); int number ; number=in.nextInt(); int result=0; do{ int diget=number%10;...

二分法(折半查找)的运用之java实现猜数字游戏

让计算机输入一个数 然后用户进行猜数游戏 一般而言,七次会猜对,如果猜不对,那么就是你的方法不对 在这儿涉及到的一个算法就是二分法 ***二分法查找,***也称为折半法,是一种在有序数组中查找特定元素的搜索算法。二分法查找的思路如下: (1)首先,从数组...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。