第一感觉是一样的,盲猜后者调用了前者,并传入参数 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() 和一样吗?的更多相关文章

  1. javase基础回顾(一)ArrayList深入解析 解读ArrayList源代码(JDK1.8.0_92)

    我们在学习这一块内容时需要注意的一个问题是 集合中存放的依然是对象的引用而不是对象本身. List接口扩展了Collection并声明存储一系列元素的类集的特性.使用一个基于零的下标,元素可以通过它们 ...

  2. 比较List和ArrayList的性能及ArrayList和LinkedList优缺点

    List和ArrayList的性能比较 在使用ArrayList这样的非泛型集合的过程中,要进行装箱和拆箱操作,会有比较大的性能损失,而使用泛型集合就没有这样的问题.List是泛型,而ArrayLis ...

  3. 记录一下显示Map<String, ArrayList<String>>中的ArrayList里的数据的操作

    1.有以下数据: ArrayList<Employee> emp = new ArrayList<>(); emp.add(new Employee("zhang&q ...

  4. 为什么是List list = new ArrayList() 而不直接用ArrayList

    为什么是List list = new ArrayList(),而不直接用ArrayList? 编程是要面向对象编程,针对抽象(接口),而非具体.List 是接口,ArrayList是实现. 实现Li ...

  5. ArrayList深度分析:ArrayList和数组间的相互转换

    一.ArrayList转换为数组ArrayList提供public <T> T[] toArray(T[] a)方法返回一个按照正确的顺序包含此列表中所有元素的数组,返回数组的运行时类型就 ...

  6. 2.请介绍一下List和ArrayList的区别,ArrayList和HashSet区别

    第一问: List是接口,ArrayList实现了List接口. 第二问: ArrayList实现了List接口,HashSet实现了Set接口,List和Set都是继承Collection接口. A ...

  7. 5.秋招复习简单整理之请介绍一下List和ArrayList的区别,arrayList和HashSet区别?

    第一问:List是接口,ArrayList是List的实现类. 第二问:ArrayList是List的实现类,HashSet是Set的实现类,List和Set都实现了Collection接口. Arr ...

  8. 解析C#中[],List,Array,ArrayList的区别及应用

    [] 是针对特定类型.固定长度的. List 是针对特定类型.任意长度的. Array 是针对任意类型.固定长度的. ArrayList 是针对任意类型.任意长度的. Array 和 ArrayLis ...

  9. ArrayList——源码探究

    摘要 ArrayList 是Java中常用的一个集合类,其继承自AbstractList并实现了List 构造器 ArrayList 提供了三个构造器,分别是 含参构造器-1 // 含参构造器-1 / ...

随机推荐

  1. 2020武汉dotNET俱乐部分享交流圆满结束

    经过长达2个多月的准备,终于在12月5日圆满的举行了武汉首届dotNET俱乐部线下分享交流活动.我们一共精心准备了3个目前比较热门的主题,分别如下: Jason分享的<ABP开发框架的扩展应用& ...

  2. 个人项目作业WC

    项目github地址 https://github.com/gs735028922gs/wordc 项目相关要求 wc.exe 是一个常见的工具,它能统计文本文件的字符数.单词数和行数.这个项目要求写 ...

  3. 在DLL中使用对话框

    在 DLL 中使用对话框资源与在 EXE 中使用是有所区别的,处理不当便会造成断言失败.原因是因为 CDialog::Create 与 CreateEx 默认使用当前进程中的资源(Dialog Tem ...

  4. 10分钟快速入门vue.js

    Vue.js是一个轻巧.高性能.可组件化的MVVM库,一套用于构建用户界面的渐进式框架,上手简单,兼容强大. 官方文档:https://cn.vuejs.org/v2/guide/ 下面我们就直接来使 ...

  5. 数据结构,哈希表hash设计实验

    数据结构实验,hash表 采用链地址法处理hash冲突 代码全部自己写,转载请留本文连接, 附上代码 #include<stdlib.h> #include<stdio.h> ...

  6. sqli-labs less1-4(union注入)

    less-1 考点:Single quotes 输入: 判断类型 ?id=1 返回loginname和password.输入的id就是与后台数据库连接的接口通过id=? 查询数据库信息 ?id=1' ...

  7. js处理浏览器兼容

    1.try  catch 在try中执行我们的代码,如果在执行的过程中发生了异常信息,我们在catch中写代替的执行方案 前提:不兼容四位情况下,执行对应的代码,需要发生异常错误才可以检测到 弊端:不 ...

  8. Web服务器-并发服务器-单进程单线程非堵塞方式(3.4.3)

    @ 目录 1.分析 2.代码 关于作者 1.分析 当socket去监听的时候,是堵塞的状态 通过tcp_sever_socket.setblocking(False)去设置不堵塞 当socket发现没 ...

  9. 教你如何修改Python的pip源镜像

    PS:在pip install安装扩展库的时候,遇到安装超时失败,可能是因为国外镜像被屏蔽了. 可以使用国内开放的pip源: 阿里云 http://mirrors.aliyun.com/pypi/si ...

  10. JQuery生成二维码,有资源下载

    原文链接:http://www.yxxrui.cn/article/65.shtml 没时间或者懒得看的,可以直接看加粗部分(或试试手感▼). 使用jquery.qrcode来生成二维码,qrcode ...