new ArrayList(0) 和 new ArrayList() 和一样吗?
第一感觉是一样的,盲猜后者调用了前者,并传入参数 0。然而,无论是 JDK 7 还是 JDK 8,这两个方法构造的结果都是不一样的。JDK 开发人员在这方面作了优化。
JDK 7
在 Java 7 中,这两个方法非常简答,ArrayList(int initialCapacity) 初始化动态数组的长度为指定的 initialCapacity,而 ArrayList() 调用了 ArrayList(int) ,传入参数 10,初始化了一个长度为 10 的数组。在 Java 7 中,后者确实调用了调用了前者,但是传入的参数并非 0,而是 10。
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
public ArrayList() {
this(10);
}
那么,为什么初始化为 10,而不是初始化为 0 呢?初始化为 0 意味着一插入元素就会触发动态数组的扩容,如果逐个增加元素,容量变化为:0, 1, 2, 3, 4, 6, 9, 13, 19, 28 ...,公式为:newCapacity = oldCapacity + (oldCapacity / 2)
。意味着如果初始化为 0,连续插入少量元素会频繁触发扩容,而 ArrayList 中插入少量元素是大概率的。所以干脆就直接初始化为 10,减少扩容的开销。
但是这样存在一个问题:无论是否使用 ArrayList 实例,只要调用了 new ArrayList(),就会初始化一个长度为 10 的 Object 数组,不管未来是否使用这个实例。JDK 7 的更新版本中和 JDK 8 中采用延迟初始化的策略解决了这个问题。
JDK 8
JDK 开发人员发现大约 80% 的时候 Java 程序员调用 new ArrayList() 来实例化 ArrayList 对象(来源:RFR JDK-7143928)。因此,延迟初始化策略能够优化大多数使用 ArrayList 的情况。
代码:
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
代码中使用了 EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 两个变量来区分 new ArrayList(0) 和 new ArrayList() 的情况,这两个成员变量默认值都是空的 Object 数组。在首次扩容的时候,根据 elementData 引用对象的不同来决定最小容量。
// 供用户使用的 public 确保容量足够的方法
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) // 不是通过 new ArrayLsit() 初始化的
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY; // 常量,值为 10
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
// 供 ArrayList 内部使用的 private 确保容量足够的方法,与上面的方法平行
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 通过 new ArrayList() 来初始化的,最小取 DEFAULT_CAPACITY (值为 10)
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
ArrayList 实例插入第 1 个元素时,如果实例是通过 new ArrayList(0) 来初始化的,计算出来的 minCapacity 为 0;如果是 new ArrayList() 构造出来的,计算出来的 minCapacity 为 DEFAULT_CAPACITY (值为 10)。然后将 minCapacity 传给 grow 方法,对数组进行扩容。
// 扩容规则
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
可以用如下图来表示从构造对象到插入第一个元素时 elementData 所引用对象的变化。
.st3 {fill:#191919;font-family:Consolas;font-size:6pt}
.st2 {fill:#191919;font-family:Consolas;font-size:8pt}
.st1 {fill:#303030;font-family:Consolas;font-size:8pt}
list1开始new ArrayList(0)elementDataEMPTY_ELEMENTDATAlist1list1.add(1)elementData1list2开始new ArrayList()elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATAlist2list2.add(1)elementData1nullnullnullnullnullnullnullnullnull
容量变化序列:
构造方式 | 容量序列 |
---|---|
new ArrayList(0) | 0, 1, 2, 3, 4, 6, 9, 13, 19, 28, 42, 63, ..., Integer.MAX_VALUE - 8 |
new ArrayList() | 0, 10, 15, 22, 33, 49, 73, 109, 163, 244, 366, ..., Integer.MAX_VALUE-8 |
MAX_ARRAY_SIZE 的值为 Integer.MAX_VALUE - 8
小结
在构造 ArrayList 实例的时候,如果没有指定动态数组的初始容量,JDK 会自动给动态数组初始化一个容量,以减少插入少量元素时动态数组的扩容开销。但这样无论是否使用实例,都会去创建一个长度为 10 的 Object 数组。开发人员发现,绝大多数情况下代码都是直接通过 new ArrayList() 来构造实例的,于是在新版本的 JDK 中使用延迟初始化的策略优化了这些情况。
在实际开发中,如果已知了 ArrayList 中需要存放的元素的数量,应该调用 new ArrayList(int initialCapacity) 方法来创建对象,这样可以消除动态数组扩容的开销,将增加元素的时间复杂度固定为 O(1)。如果存放数量未知,则调用 new ArrayList() 来创建对象,代码中已经通过设置初始容量、延迟初始化的方式使效率尽可能高。
new ArrayList(0) 和 new ArrayList() 和一样吗?的更多相关文章
- javase基础回顾(一)ArrayList深入解析 解读ArrayList源代码(JDK1.8.0_92)
我们在学习这一块内容时需要注意的一个问题是 集合中存放的依然是对象的引用而不是对象本身. List接口扩展了Collection并声明存储一系列元素的类集的特性.使用一个基于零的下标,元素可以通过它们 ...
- 比较List和ArrayList的性能及ArrayList和LinkedList优缺点
List和ArrayList的性能比较 在使用ArrayList这样的非泛型集合的过程中,要进行装箱和拆箱操作,会有比较大的性能损失,而使用泛型集合就没有这样的问题.List是泛型,而ArrayLis ...
- 记录一下显示Map<String, ArrayList<String>>中的ArrayList里的数据的操作
1.有以下数据: ArrayList<Employee> emp = new ArrayList<>(); emp.add(new Employee("zhang&q ...
- 为什么是List list = new ArrayList() 而不直接用ArrayList
为什么是List list = new ArrayList(),而不直接用ArrayList? 编程是要面向对象编程,针对抽象(接口),而非具体.List 是接口,ArrayList是实现. 实现Li ...
- ArrayList深度分析:ArrayList和数组间的相互转换
一.ArrayList转换为数组ArrayList提供public <T> T[] toArray(T[] a)方法返回一个按照正确的顺序包含此列表中所有元素的数组,返回数组的运行时类型就 ...
- 2.请介绍一下List和ArrayList的区别,ArrayList和HashSet区别
第一问: List是接口,ArrayList实现了List接口. 第二问: ArrayList实现了List接口,HashSet实现了Set接口,List和Set都是继承Collection接口. A ...
- 5.秋招复习简单整理之请介绍一下List和ArrayList的区别,arrayList和HashSet区别?
第一问:List是接口,ArrayList是List的实现类. 第二问:ArrayList是List的实现类,HashSet是Set的实现类,List和Set都实现了Collection接口. Arr ...
- 解析C#中[],List,Array,ArrayList的区别及应用
[] 是针对特定类型.固定长度的. List 是针对特定类型.任意长度的. Array 是针对任意类型.固定长度的. ArrayList 是针对任意类型.任意长度的. Array 和 ArrayLis ...
- ArrayList——源码探究
摘要 ArrayList 是Java中常用的一个集合类,其继承自AbstractList并实现了List 构造器 ArrayList 提供了三个构造器,分别是 含参构造器-1 // 含参构造器-1 / ...
随机推荐
- 2020武汉dotNET俱乐部分享交流圆满结束
经过长达2个多月的准备,终于在12月5日圆满的举行了武汉首届dotNET俱乐部线下分享交流活动.我们一共精心准备了3个目前比较热门的主题,分别如下: Jason分享的<ABP开发框架的扩展应用& ...
- 个人项目作业WC
项目github地址 https://github.com/gs735028922gs/wordc 项目相关要求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数.这个项目要求写 ...
- 在DLL中使用对话框
在 DLL 中使用对话框资源与在 EXE 中使用是有所区别的,处理不当便会造成断言失败.原因是因为 CDialog::Create 与 CreateEx 默认使用当前进程中的资源(Dialog Tem ...
- 10分钟快速入门vue.js
Vue.js是一个轻巧.高性能.可组件化的MVVM库,一套用于构建用户界面的渐进式框架,上手简单,兼容强大. 官方文档:https://cn.vuejs.org/v2/guide/ 下面我们就直接来使 ...
- 数据结构,哈希表hash设计实验
数据结构实验,hash表 采用链地址法处理hash冲突 代码全部自己写,转载请留本文连接, 附上代码 #include<stdlib.h> #include<stdio.h> ...
- sqli-labs less1-4(union注入)
less-1 考点:Single quotes 输入: 判断类型 ?id=1 返回loginname和password.输入的id就是与后台数据库连接的接口通过id=? 查询数据库信息 ?id=1' ...
- js处理浏览器兼容
1.try catch 在try中执行我们的代码,如果在执行的过程中发生了异常信息,我们在catch中写代替的执行方案 前提:不兼容四位情况下,执行对应的代码,需要发生异常错误才可以检测到 弊端:不 ...
- Web服务器-并发服务器-单进程单线程非堵塞方式(3.4.3)
@ 目录 1.分析 2.代码 关于作者 1.分析 当socket去监听的时候,是堵塞的状态 通过tcp_sever_socket.setblocking(False)去设置不堵塞 当socket发现没 ...
- 教你如何修改Python的pip源镜像
PS:在pip install安装扩展库的时候,遇到安装超时失败,可能是因为国外镜像被屏蔽了. 可以使用国内开放的pip源: 阿里云 http://mirrors.aliyun.com/pypi/si ...
- JQuery生成二维码,有资源下载
原文链接:http://www.yxxrui.cn/article/65.shtml 没时间或者懒得看的,可以直接看加粗部分(或试试手感▼). 使用jquery.qrcode来生成二维码,qrcode ...