关于ArrayList的扩容机制


ArrayList作为List接口常用的一个实现类,其底层数据接口由数组实现,可以保证O(1) 复杂度的随机查找, 在增删效率上不如LinkedList,但是在查询效率较高,相对同是数组实现的Vector,并不能保证线程安全,所以多适用于单线程环境。

由于底层是有数组实现,因为数组的长度需要初始化定义,并不能自动进行长度增加,所以ArrayList有对应的扩容机制,当增加元素时,会判断是否需要扩容,下面看源码:

首先认识ArrayList的几个重要变量:

  1. /*序列化ID*/
  2. private static final long serialVersionUID = 8683452581122892189L;
  3. /*默认的数组容量,第一次进行添加操作时,如果ArrayList为空或者添加元素后的数组长度小于等于10,会将该值作为数组的初始长度*/
  4. private static final int DEFAULT_CAPACITY = 10;
  5. /*一个空的对象数组,在需要将elementData数组置空时,会将该对象赋予elementData*/
  6. private static final Object[] EMPTY_ELEMENTDATA = {};
  7. /*默认的空对象数组,在ArrayList构造时,默认将该对象给elementData*/
  8. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  9. /*ArrayList内部用于存储元素的数组,transient关键字修饰,不使用默认序列化*/
  10. transient Object[] elementData; // non-private to simplify nested class access
  11. /*数组的长度,是ArrayList中实际元素的个数,不是数组的容量*/
  12. private int size;
  1. ArrayList的尾部插入和其他位置虽然是不同方法,但是都使用到了ensureCapacityInternal()方法确保数组内部容量,在每次添加操作中都会使用该方法进行容量判断,之后,才会将增加的元素添加到数组中,下面以尾部插入为例;
  1. /**
  2. * 增加数据元素到集合得末尾
  3. *
  4. */
  5. public boolean add(E e) {
  6. /*判断是否扩容,如果原来的元素个数是size,那么增加一个元素之后的元素个数为size + 1,所以需要的最小容量就为size + 1*/
  7. ensureCapacityInternal(size + 1); // Increments modCount!!
  8. elementData[size++] = e;
  9. return true;
  10. }

ensureCapacityInternal()将判断委托给ensureExplicitCapacity()处理

  1. /*获取数组最小容量*/
  2. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  3. /*如果elementData为空,且minCapacity <= 10,都会以DEFAULT_CAPACITY作为最小容量*/
  4. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
  5. return Math.max(DEFAULT_CAPACITY, minCapacity);
  6. }
  7. return minCapacity;
  8. }
  9.  
  10. private void ensureCapacityInternal(int minCapacity) {
  11. /*ensureCapacityInternal方法委托给ensureExplicitCapacity方法*/
  12. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  13. }
  14.  
  15. private void ensureExplicitCapacity(int minCapacity) {
  16. modCount++;
  17.  
  18. /*如果minCapacity大于elementData的长度,使用grow方法进行扩容*/
  19. if (minCapacity - elementData.length > 0)
  20. grow(minCapacity);
  21. }

下面就是扩容的实现方法grow方法:

  1. /*扩容方法*/
  2. private void grow(int minCapacity) {
  3. /*原有数组容量*/
  4. int oldCapacity = elementData.length;
  5. /*新的数组容量,下面位运算相当于newCapacity = oldCapacity * 1.5 向下取整*/
  6. int newCapacity = oldCapacity + (oldCapacity >> 1);
  7. /*如果新的数组容量小于需要的最小容量,即假设新的数组容量是15,最小需要16的容量,则会将16赋予newCapacity*/
  8. if (newCapacity - minCapacity < 0)
  9. newCapacity = minCapacity;
  10. /*变量MAX_ARRAY_SIZE = 2147483639 [0x7ffffff7],如果扩容后的新容量大于这个值则会使用hugeCapacity方法
  11. * 判断最小容量minCapacity是否大于MAX_ARRAY_SIZE,如果需要最小容量的也大于MAX_ARRAY_SIZE,则会以
  12. * Integer.MAX_VALUE = 2147483647 [0x7fffffff]的值最为数组的最大容量,如果没有则会以MAX_ARRAY_SIZE最为最大容量
    * MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,为什么用MAX_ARRAY_SIZE ,源码的中的说法是一些虚拟机中会对数组保留一些标题字段
    * 使用Integer.MAX_VALUE会造成内存溢出错误
  13. * */
  14. if (newCapacity - MAX_ARRAY_SIZE > 0)
  15. newCapacity = hugeCapacity(minCapacity);
  16. /*确定数组最终的容量newCapacity之后,将原有ArrayList的元素全部拷贝到一个新的ArrayList中*/
  17. elementData = Arrays.copyOf(elementData, newCapacity);
  18. }
  19. private static int hugeCapacity(int minCapacity) {
  20. /*如果minCapacity小于0,则抛出内存溢出错误*/
  21. if (minCapacity < 0)
  22. throw new OutOfMemoryError();
  23. return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
  24. }

ArrayList的扩容机制还是相对容易理解的,就是在第一个添加元素时,创建一个长度为10的数组,之后随着元素的增加,以1.5倍原数组的长度创建一个新数组,即10, 15, 22, 33,。。这样序列建立,将原来的元素拷贝到新数组之中,如果数组长度达到上限,则会以

  1. MAX_ARRAY_SIZE 或者 Integer.MAX_VALUE作为最大长度,而多余的元素就会被舍弃掉。

关于ArrayList的扩容机制的更多相关文章

  1. ArrayList的扩容机制

    一.ArrayList的扩容机制 1.扩容的计算方式是向右位移,即:newSize = this.size + (this.size>>1).向右位移,只有在当前值为偶数时,才是除以2:奇 ...

  2. 浅谈 ArrayList 及其扩容机制

    浅谈ArrayList ArrayList类又称动态数组,同时实现了Collection和List接口,其内部数据结构由数组实现,因此可对容器内元素实现快速随机访问.但因为ArrayList中插入或删 ...

  3. 【数组】- ArrayList自动扩容机制

    不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力, ...

  4. Java ArrayList自动扩容机制

    动态扩容 1.add(E e)方法中 ①  ensureCapacityInternal(size+1),确保内部容量,size是添加前数组内元素的数量 ②  elementData[size++] ...

  5. ArrayList动态扩容机制

    初始化:有三种方式 1.默认的构造器,将会以默认的大小来初始化内部的数组:public ArrayList(); 2.用一个ICollection对象来构造,并将该集合的元素添加到ArrayList: ...

  6. 学习ArrayList的扩容机制

    基于jdk8 1.首先我们看new ArrayList中 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDA ...

  7. 小白也能看懂的ArrayList的扩容机制

    来,话不多说进入正题!我们下面用最简单的代码创建ArrayList并添加11个元素,并 一 一 讲解底层源码:在说之前,给大家先普及一些小知识: >ArrayList底层是用数组来实现的 > ...

  8. 送分题,ArrayList 的扩容机制了解吗?

    1. ArrayList 了解过吗?它是啥?有啥用? 众所周知,Java 集合框架拥有两大接口 Collection 和 Map,其中,Collection 麾下三生子 List.Set 和 Queu ...

  9. ArrayList源码解析(二)自动扩容机制与add操作

    本篇主要分析ArrayList的自动扩容机制,add和remove的相关方法. 作为一个list,add和remove操作自然是必须的. 前面说过,ArrayList底层是使用Object数组实现的. ...

随机推荐

  1. EntityFrameworkCore 单表树状结构配置

    数据结构 public class TreeNode { [Key] public long Id { get; set; } public string NodeName { get; set; } ...

  2. MVC 添加数据的三种方法

    1 通过Request接收数据,进行添加 <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<d ...

  3. WPF中类似使用tab键功能,可以向上向下定位

    原文:WPF中类似使用tab键功能,可以向上向下定位 private void tbYyrs_KeyUp(object sender, KeyEventArgs e) { UIElement elem ...

  4. Android微信支付SDK

    App对接微信调起微信支付需要在微信平台注册,鉴别的标识就是App的包名,所以将申请的包名单独打包成一个Apk文件,则在其他的App调起此Apk的时候同样可以起到调用微信支付的功能.这样就实现了调起微 ...

  5. SAP和去哪儿网面试感悟

    这两天先后面了两家公司,个人感觉挺有意思,就写下来.现在是4月份,校招基本没有了,去哪儿网刚好有春招,我就去试试.SAP是同学推荐的. 去哪儿.参加笔试,个人感觉还行.半个月后收到面试通知.到了面试现 ...

  6. delphi 线程教学第一节:初识多线程(讲的比较浅显),还有三个例子

    http://www.cnblogs.com/lackey/p/6297115.html 几个例子: http://www.cnblogs.com/lackey/p/5371544.html

  7. HTTP协议解析(格式和举例十分清楚)

    掌握HTTP虽然不是必须的,但是如果你知道它的工作原理,那么在学习JSP开发中的某些知识就可以易如反掌了. 一,HTTP协议详解之URL篇 http(超文本传输协议)是一个基于请求与响应模式的.无状态 ...

  8. IDHTTP-HTTPS通讯方法(Indy10.2.3之前仅支持OpenSSL的0.9.8以前的版本)

    加载https的站点页面内容的时候记得在窗体上加上“IdSSLIOHandlerSocketOpenSSL1” 以及idhttp里面IOHandler为“IdSSLIOHandlerSocketOpe ...

  9. Linux kernel version dirty

    在我们使用git来管理Linux Kernel的时候,在编译的时候会在你的kernel version加上git commit number 有时候还会出现dirty字样,字面意思是内核被污染的意思. ...

  10. RESTful API设计原则与规范

    RESTful API设计原则与规范 一.背景与基础概念 2 二.RESTful API应遵循的原则 3 1.协议(Protocol) 3 2.域名(ROOT URL) 3 3.版本(Versioni ...