点击上方蓝色“方志朋”,选择“设为星标”

回复“666”获取独家整理的学习资料!

注:本系列文章中用到的jdk版本均为java8

ArrayList类图如下:

ArrayList的底层是由数组实现的,数组的特点是固定大小,而ArrayList实现了动态扩容

ArrayList部分变量如下,在下面的分析中会用到这些变量。

/*** 默认容量*/
private static final int DEFAULT_CAPACITY = 10;
/*** 空的对象数组*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/*** 无参构造器创建的空数组*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/*** 存放数据的数组的缓存变量*/
transient Object[] elementData;
/*** 元素数量*/
private int size;

一 初始化ArrayList

初始化ArrayList一般会使用以下两个构造器

1.1 无参构造器

初始化ArrayList的时候如果不指定大小,则会创建一个空数组。

public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

1.2 指定数组大小的构造器

创建一个预估大小的数组,指定大小后只是指定了数组初始值的大小,不影响后面扩容,指定的好处就是可以节省内存及时间上的开销。

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);}
}

二 添加元素、动态扩容

ArrayList.add(E e)源码:

public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}

add()elementData[size++] = e很好理解,就是将元素插入第size个位置,然后将size++,我们重点来看看ensureCapacityInternal(size + 1)方法;

private void ensureCapacityInternal(int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);
}

ensureCapacityInternal()方法中判断缓存变量elementData是否为空,也就是判断是否是第一次添加元素,如果是第一次添加元素,则设置初始化大小为默认容量10,否则为传入的参数。这个方法的目的就是获取初始化数组容量。获取到初始化容量后调用ensureExplicitCapacity(minCapacity)方法;

private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}

ensureExplicitCapacity(minCapacity)方法用来判断是否需要扩容,假如第一次添加元素,minCapacity10elementData容量为0,那么就需要去扩容。调用grow(minCapacity)方法。

// 数组的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;// 扩容大小为原来数组长度的1.5倍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);
}

grow(minCapacity)方法对数组进行扩容,扩容大小为原数组的1.5倍,如果计算出的扩容容量比需要的容量小,则扩容大小为需要的容量,如果扩容容量比数组最大容量大,则调用hugeCapacity(minCapacity)方法,将数组扩容为整数的最大长度,然后将elemetData数组指向新扩容的内存空间并将元素复制到新空间。

当需要的集合容量特别大时,扩容1.5倍就会非常消耗空间,因此建议初始化时预估一个容量大小。

三 删除元素

ArrayList提供两种删除元素的方法,可以通过索引元素进行删除。两种删除大同小异,删除元素后,将后面的元素一次向前移动。

ArrayList.remove(int index)源码:

public E remove(int index) {rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its workreturn oldValue;
}

删除元素时,首先会判断索引是否大于ArrayList的大小,如果索引范围正确,则将索引位置的下一个元素赋值到索引位置,将ArrayList的大小-1,最后返回移除的元素。操作图如下,假如我要移除索引为1的元素:

四 总结

ArrayList底层是数组实现的,可以进行动态扩容,扩容大小为原来的1.5倍,虽然可以通过动态扩容,但是数组非常大时会特别浪费空间,因此建议初始化时预估数组大小。ArrayList允许插入重复值和空值。ArrayList实现了RandomAccess接口,支持快速随机访问,就是可以通过索引快速查到某个元素,因此遍历时使用for循环的方式效率更高。ArrayList是线程不安全的,可以通过Collections.synchronizedList将其转变为线程安全的集合,不过一般不会使用,VectorCopyOnWriteArrayList是线程安全的,Vector性能一般,逐渐被CopyOnWriteArrayList取代了。

热门内容:
  • IntelliJ IDEA 2020.3 重大特性

  • 用了3年CAT,这次我想选择SkyWalking,老板反手就是一个赞!

  • 面试官:String长度有限制吗?是多少?

  • 工作10年后,再看String s = new String("xyz") 创建了几个对象?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

从面试角度分析ArrayList源码相关推荐

  1. 从面试角度分析CopyOnWriteArrayList源码

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 注:本系列文章中用到的jdk版本均为java8 相比很多同 ...

  2. 从面试角度分析LinkedList源码

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 注:本系列文章中用到的jdk版本均为java8 Linke ...

  3. 面试必备:ArrayList源码解析(JDK8)

    概述 很久没有写博客了,准确的说17年以来写博客的频率降低到一个不忍直视的水平.这个真不怪我,给大家解释一下.  一是自从做了leader,整天各种事,开会,过需求,无限循环.心很累,时间也被无线压榨 ...

  4. java list addall源码_Java集合:ArrayList源码分析

    其实我看到已有很多大佬写过此类文章,并且写的也比较清晰明了,那我为何要再写一遍呢?其实也是为了加深本身的印象,巩固本身的基础html (主要是不少文章没有写出来我想知道的东西!!!​!!!!)java ...

  5. Java集合Collection源码系列-ArrayList源码分析

    Java集合系列-ArrayList源码分析 文章目录 Java集合系列-ArrayList源码分析 前言 一.为什么想去分析ArrayList源码? 二.源码分析 1.宏观上分析List 2.方法汇 ...

  6. 面试必备:ArrayMap源码解析

    想看我更多文章:[张旭童的博客]http://blog.csdn.net/zxt0601 想来gayhub和我gaygayup:[mcxtzhang的Github主页]https://github.c ...

  7. java function获取参数_「Java容器」ArrayList源码,大厂面试必问

    ArrayList简介 ArrayList核心源码 ArrayList源码分析 System.arraycopy()和Arrays.copyOf()方法 两者联系与区别 ArrayList核心扩容技术 ...

  8. 面试官系统精讲Java源码及大厂真题 - 05 ArrayList 源码解析和设计思路

    05 ArrayList 源码解析和设计思路 耐心和恒心总会得到报酬的. --爱因斯坦 引导语 ArrayList 我们几乎每天都会使用到,但真正面试的时候,发现还是有不少人对源码细节说不清楚,给面试 ...

  9. ArrayList源码分析与手写

    本节主要分析JDK提供的ArrayList的源码,以及与自己手写的ArrayList进行对比. ArrayList源码分析 构造方法 private static final int DEFAULT_ ...

最新文章

  1. C#把数据写到硬盘指定位置
  2. DL之CNN:利用CNN(keras, CTC loss, {image_ocr})算法实现OCR光学字符识别
  3. QT的QWriteLocker类的使用
  4. 解决mysql表不能查询修改删除等操作并出现卡死
  5. 转!Java关键字final、static使用总结
  6. 使用OutputDebugString帮助调试
  7. 在Java中衡量执行时间– Spring StopWatch示例
  8. C# 判断txt文件编码格式
  9. linux跟踪内存块,在Linux程序中跟踪活动使用的内存
  10. 清华博士的逆袭路:从延毕警告到在顶刊发文,我蛰伏了四年
  11. linux如何制作服务,linux把jar做成服务
  12. PHP_CodeSniffer HG 服务端部署篇
  13. master slave mysql_MYSQL高可用之复制(MASTER/SLAVE)
  14. 联想双显卡用户Win8下独显被禁止解决方案
  15. 毕设题目:Matlab疾病识别与分类
  16. 目标跟踪论文整理(不全,以单目标为主)
  17. 微信获取openid出现40163
  18. (图文教程)帝国cms7.0列表页模板调用多说评论次数
  19. 中大计算机学院不及格,大学要求越来越严格 | 学渣可能无法从中大毕业了
  20. 如何回复审稿人的意见?(总结)

热门文章

  1. react遇到的各种坑
  2. Swift中使用typealias定义一个闭包closure
  3. struts2 实现自定义标签
  4. 每天一个linux命令(33):df 命令
  5. if for switch语句
  6. 写给还在大学的兄弟姐妹
  7. vs2008 常用快捷键
  8. 【青少年编程】【三级】计算平均分
  9. 【ACM】连续出现的字符
  10. 在 Python 中妙用短路机制