集合章节
请问ArrayList/LinkedList/Vector的异同?
谈谈你的理解?
ArrayList底层是什么?
扩容机制?
Vector和ArrayList的最大区别?
ArrayList和LinkedList的异同
二者都线程不安全,相对线程安全的Vector,执行效率高。此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。
ArrayList和Vector的区别
Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。
ArrayList实现类
-
ArrayList 是 List 接口的典型实现类、主要实现类
-
本质上,ArrayList是对象引用的一个”变长”数组
-
Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 实例,也不是Vector 实例。 Arrays.asList(…) 返回值是一个固定长度的 List 集合
LinkedList实现类
-
内部实现:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:
-
prev变量记录前一个元素的位置
-
next变量记录下一个元素的位置
-
-
对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
Vector实现类
-
Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList 相同,区别之处在于Vector是线程安全的。
-
引用:https://www.imooc.com/article/278549
ArrayList的扩容机制
初始容量
ArrayList
有多个不同的构造函数,不同的构造函数的初始容量是不同的。快速看一下ArrayList
源码里关于元素存放的几个私有属性:
// 默认容量是10 private static final int DEFAULT_CAPACITY = 10; // 如果容量为0的时候,就返回这个数组 private static final Object[] EMPTY_ELEMENTDATA = {}; // 使用默认容量10时,返回这个数组 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 元素存放的数组 transient Object[] elementData; // 元素的个数 private int size; // 记录被修改的次数 protected transient int modCount = 0; // 数组的最大值 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
ArrayList
有三个构造方法,不同的构造方法的容量是不一样的,具体可以查看JDK 源码。
- 如果不传入初始容量,就使用默认容量,并设置
elementData
为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- 如果传入初始容量,会判断这个传入的值,如果大于0,就new一个新的Object数组,如果等于0,就直接设置
elementData
为EMPTY_ELEMENTDATA
。 - 如果传入一个Collection,则会调用
toArray()
方法把它变成一个数组并赋值给elementData
。同样会判断它的长度是否为0,如果为0,设置elementData
为EMPTY_ELEMENTDATA
。
扩容具体指的是什么?
可以看到,ArrayList
里面有两个概念,一个是capacity
,它表示的就是“容量”,其实质是数组elementData
的长度。而size
则表示的“存放的元素的个数”。
因为Java中,数组操作不能越界,所以我们必须要保证在插入操作的时候,不会抛出数组越界异常。
ArrayList是如何实现扩容的?
底层主要是这三个私有方法:
// 扩容一个 private Object[] grow() { return grow(size + 1); } // 保证扩容到期望容量minCapacity及以上 private Object[] grow(int minCapacity) { return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); } // 根据期望容量minCapacity计算实际需要扩容的容量 private int newCapacity(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; // 得到旧容量 int newCapacity = oldCapacity + (oldCapacity >> 1); // 设置新容量为旧容量的1.5倍 if (newCapacity - minCapacity <= 0) { // 如果新容量仍然小于期望容量 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // 如果是使用的默认容量 return Math.max(DEFAULT_CAPACITY, minCapacity); // 取默认容量和期望容量较大值返回 if (minCapacity < 0) // overflow // 检查期望容量是否越界(int 的范围) throw new OutOfMemoryError(); return minCapacity; // 返回期望容量 } // 如果新容量大于期望容量,判断一下新容量是否越界 return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity); }
可以看到,底层其实是调用了Arrays.copyOf
方法来进行扩充数组容量的。这里我们主要看一下最后一个方法newCapacity(int minCapacity)的实现。
默认情况下,新的容量会是原容量的1.5倍,这里用了位运算提高效率。一般情况下,如果扩容1.5倍后就大于期望容量,那就返回这个1.5倍旧容量的值。而如果小于期望容量,那就返回期望容量。这里对默认容量10做了特殊处理。
使用1.5倍这个数值而不是直接使用期望容量,是为了防止频繁扩容影响性能。试想如果每次add操作都要扩容一次,那性能将会非常低下。
上述grow方法其实主要是用于实现自动扩容的。而用户也可以通过调用以下方法实现手动扩容:
public void ensureCapacity(int minCapacity) { if (minCapacity > elementData.length && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA && minCapacity <= DEFAULT_CAPACITY)) { modCount++; grow(minCapacity); } }
为什么需要手动扩容?试想一下,如果用户已经知道即将存入大量的元素,比如目前有20个元素,即将存入2000个。那这个时候使用自动扩容就会扩容多次。而手动扩容可以一次性扩容到2000,可以提高性能。
ArrayList有缩容吗?
ArrayList
没有缩容。无论是remove
方法还是clear
方法,它们都不会改变现有数组elementData
的长度。但是它们都会把相应位置的元素设置为null
,以便垃圾收集器回收掉不使用的元素,节省内存。
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
Java入门到入坟
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南