JDK1.8中ArrayList是如何扩容的

作者:Ccy丶双 时间:2023-01-20 19:04:47 

ArrayList简介:

ArrayList实现了List接口它是一个可调整大小的数组可以用来存放各种形式的数据。并提供了包括CRUD在内的多种方法可以对数据进行操作但是它不是线程安全的,外ArrayList按照插入的顺序来存放数据。

在讲扩容机制之前,我们需要了解一下ArrayList中最主要的几个变量:


private static final int DEFAULT_CAPACITY = 10;//数组默认初始容量

private static final Object[] EMPTY_ELEMENTDATA = {};//定义一个空的数组实例以供其他需要用到空数组的地方调用

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//定义一个空数组,跟前面的区别就是这个空数组是用来判断ArrayList第一添加数据的时候要扩容多少。默认的构造器情况下返回这个空数组

transient Object[] elementData;//数据存的地方它的容量就是这个数组的长度,同时只要是使用默认构造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA )第一次添加数据的时候容量扩容为DEFAULT_CAPACITY = 10

private int size;//当前数组的长度

本题的所有的讲解都是基于JDK8

JDK1.8中ArrayList是如何扩容的

这道题考察了ArrayList的构造器和对扩容机制的了解,本篇博客基于此出发讲解ArrayList的扩容机制

想要做出这道题必须了解ArrayList的构造函数,ArrayList的构造函数总共有三个:

  • ArrayList()构造一个空的数组。

    JDK7中构造一个初始容量为10的空列表但是JDK8中只是构造一个空的数组

  • ArrayList(Collection<? extends E> c)构造一个包含指定 collection 的元素的数组,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

  • ArrayList(int initialCapacity)构造一个具有指定初始容量的空数组。

我们重点来看这两个ArrayList(int initialCapacity)ArrayList()构造函数


private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
   this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

初始化一个空数组,这是JDK8不同于之前版本的地方


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

对于形参initialCapacity判断,如果大于0那么就声明一个和形参一样大小的数组。了解到这里似乎这道题的正确答案也出来了即选择A,并没有发生扩容

但是作为一名合格的程序员要有探索精神,题目提到了扩容,既然ArrayList底层是一个数组,那么就肯定会满,什么时候发生扩容呢?


//1.add方法为添加元素在数组末尾
public boolean add(E e) {
   //确保数组容量 size指向数组的末尾
   ensureCapacityInternal(size + 1);
   //在完成添加之前要确保数组长度足够
   elementData[size++] = e;
   return true;
}
//3.elementData为ArrayList底层维护的数组,minCapacity为此时数组的大小
private static int calculateCapacity(Object[] elementData, int minCapacity) {
   //如果数组为初始化的值,就初始化数组容量为10(空参的构造方法下首次添加)
   if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
       return Math.max(DEFAULT_CAPACITY, minCapacity);
   }
   return minCapacity;
}
//2.minCapacity表示此时数组的大小
private void ensureCapacityInternal(int minCapacity) {
   ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//4.minCapacity表示此时数组的大小
private void ensureExplicitCapacity(int minCapacity) {
   modCount++;
   //如果此时数组容量的大小不够就扩容
   if (minCapacity - elementData.length > 0)
       grow(minCapacity);
}

源码读到这里,我们明白了,当我们每次向ArrayList添加元素的时候,都会首先确保数组容量够放下元素如果不够就会 grow(minCapacity)调用扩容函数,那么秉承着探索的精神,原本大小的数组扩容之后变成多大了呢?还得继续看源码


//扩容源码
private void grow(int minCapacity) {
   //获取当前数组的长度
   int oldCapacity = elementData.length;
   //>>右移相当于整除2,新容量相当于就旧容量的1.5倍
   int newCapacity = oldCapacity + (oldCapacity >> 1);
   //如果扩容后的容量还不够那么就以需要的容量为新容量
   if (newCapacity - minCapacity < 0)
       newCapacity = minCapacity;
   //如果新容量已经超过最大容量了,那么就直接使用最大容量
   if (newCapacity - MAX_ARRAY_SIZE > 0)
       newCapacity = hugeCapacity(minCapacity);
   //讲新容量的数组拷贝
   elementData = Arrays.copyOf(elementData, newCapacity);
}

源码大致读完后,我们明白了ArrayList的自动扩容机制,每次新添加元素的时候都会判断是否能够容下,如果不够就会发生扩容,扩容的大小为原大小的1.5倍数,明白这些以后让我们看看下面这段程序扩容了几次呢??容量是多少呢?


ArrayList<Integer> arrayList = new ArrayList<Integer>(20);
for(int i=1;i<=50;i++) {
    arrayList.add(i);
}

前20次添加不会发生扩容,当21元素添加时数组容量从20扩容到30,当添加31元素时数组容量从30扩容到45,当添加46元素时数组容量从45扩容到67

来源:https://blog.csdn.net/goddessccy/article/details/121863522

标签:JDK1.8,ArrayList,扩容
0
投稿

猜你喜欢

  • java实现一个简单的网络爬虫代码示例

    2021-08-05 13:59:12
  • SpringBoot如何获取Kafka的Topic列表

    2023-11-26 16:01:52
  • 超详细的Spring Boot入门笔记(总结)

    2022-10-26 18:44:21
  • Spring Cloud Ribbon配置详解

    2023-11-25 01:32:50
  • Java基础将Bean属性值放入Map中的实例

    2023-10-11 13:57:40
  • Spring Boot统一异常处理最佳实践(拓展篇)

    2023-10-29 16:00:04
  • Springboot轻量级的监控组件SpringbootAdmin

    2023-08-25 10:08:31
  • springboot整合JSR303参数校验与全局异常处理的方法

    2023-10-06 01:31:40
  • 实例解析JAVA中代码的加载顺序

    2021-10-26 14:57:22
  • 详解如何在Java中实现堆排序算法

    2023-11-11 11:34:46
  • springboot整合EHCache的实践方案

    2023-08-23 23:48:31
  • 浅谈AnDroidDraw+DroidDraw实现Android程序UI设计的分析说明

    2023-09-28 20:41:51
  • 详解SpringBoot定制@ResponseBody注解返回的Json格式

    2023-07-26 13:47:02
  • java打印指定年月的日历

    2023-11-11 19:21:19
  • Springboot整合微信支付(订单过期取消及商户主动查单)

    2023-05-15 23:40:50
  • 关于SpringBoot使用Redis空指针的问题(不能成功注入的问题)

    2023-09-04 01:30:03
  • springboot的yml配置文件通过db2的方式整合mysql的教程

    2023-08-06 04:28:55
  • 一看就懂的Android APP开发入门教程

    2023-07-18 04:10:41
  • Java实现PDF转为Word文档的示例代码

    2021-10-05 02:19:26
  • Android TextView中文字通过SpannableString设置属性用法示例

    2023-07-26 07:11:51
  • asp之家 软件编程 m.aspxhome.com