ArrayList的动态扩容的实现

ArrayList可以实现容量的自适应的增加,通过阅读源代码,对这个机制进行一下简单的分析。

首先,ArrayList有一个初始的默认大小,为10.

private static final int DEFAULT_CAPACITY = 10;

从add方法为入口

public boolean add(E e) {

ensureCapacityInternal(size + 1);  // Increments modCount!!

elementData[size++] = e;

return true;

}

可见,在添加元素之前,会先调用ensureCapacityInternal这个方法,那就再进到这个这个方法中去。

private void ensureCapacityInternal(int minCapacity) {

if (elementData == EMPTY_ELEMENTDATA) {

minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);

}

ensureExplicitCapacity(minCapacity);

}

首先,看看数组是否为空,如果是,就将DEFAULT_CAPACITY和minCapacity的较大的一个作为初始大小赋值给minCapacity ,DEFAULT_CAPACITY是10,minCapacity就是add方法中传入的size
+ 1。

如果数组不为空,就直接执行ensureExplicitCapacity方法。ensureExplicitCapacity方法的实现如下:

private void ensureExplicitCapacity(int
minCapacity) {

modCount++;

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

在这个方法里,会比较minCapacity
与elementData.length 的大小。当第一次插入值时,由于minCapacity 一定大于等于10,而

elementData.length是0,所以会去继续执行grow方法,那就继续进入到这个方法中去。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);

}

这个方法首先计算出一个容量,大小为oldCapacity + (oldCapacity >> 1)。即elementData数组长度的1.5倍。再从minCapacity 和

这个容量中取较大的值作为扩容后的新的数组的大小。

这时,会出现两种情况:

一. 新的容量小于数组的最大值MAX_ARRAY_SIZE ,即

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

最大的容量之所以设为Integer.MAX_VALUE - 8,在定义上方的注释中已经说了,大概是一些JVM实现时,会在数组的前面放一些额外的数据,再加上数组中的数据大小,有可能超过一次能申请的整块内存的大小上限,出现OutOfMemoryError。

二. 新的容量大于数组的最大值MAX_ARRAY_SIZE

这时,又会进入另一个方法hugeCapacity

private static int hugeCapacity(int minCapacity) {

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return (minCapacity > MAX_ARRAY_SIZE) ?

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

会对minCapacity 和 MAX_ARRAY_SIZE进行比较,minCapacity 大的话,就将Integer.MAX_VALUE 作为新数组的大小,否则将MAX_ARRAY_SIZE作为数组的大小。

最后,就把原来数组的数据复制到新的数组中。调用了Arrays的copyOf方法。内部是System的arraycopy方法,由于是native方法,所以效率较高。

通过分析可以发现,ArrayList的扩容会产生一个新的数组,将原来数组的值复制到新的数组中。会消耗一定的资源。所以我们初始化ArrayList时,最好可以估算一个初始的大小。

时间: 2024-10-13 11:35:37

ArrayList的动态扩容的实现的相关文章

Arraylist动态扩容详解

ArrayList 概述 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长. ArrayList不是线程安全的,只能用在单线程环境下. 实现了Serializable接口,因此它支持序列化,能够通过序列化传输: 实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问: 实现了Cloneable接口,能被克隆. 动态扩容 一 初始化 首先有三种方式来初始化: public ArrayList(); 默认的构造器,将会以默认的大小来初始化内部的数

ArrayList动态扩容大小

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_SI

docker容器端口IP规划及端口动态扩容

docker容器一旦启动,参数就无法改变,生产环境中最常变的就是端口映射,为了解决这个问题,那么首先就要规划好,本文列出了两种端口规划方案,如果后续维护中出现了要增加端口映射的场景,本文也给出了动态端口映射扩容方案. 1.单IP多容器映射规划方案 此种环境适用只有单个IP环境下,如云主机等. 1.1 端口映射规划表格:     规划不同的端口段,映射到容器从而对外提供服务. 主机名称 ssh映射 mysql映射 nginx映射 redis映射 redis-test 51000 22 51001

&lt;实训|第十二天&gt;用LVM对linux分区进行动态扩容

[[email protected]~]#序言在linux中,我们安装软件的途径一般有那些,你们知道吗?在linux中,如果你的磁盘空间不够用了,你知道如何来扩展磁盘吗?动态扩容不仅在工作中还是在其他方面都是一个非常重要的技能,所以学会它,对你很有帮助.  开班第十二天: [[email protected]~]#今天的课程大纲 1.linux系统中安装软件的方式和途径 2.lvm磁盘管理机制的原理 3.如何使用lvm管理磁盘 4.动态扩容的方法 5.使用lvm的好处 详细讲解: [[email

云主机磁盘存储动态扩容(LVM)方案

一.场景 云主机使用的越来越多,现在几乎没有不使用云主机的,经常会碰到需要升级硬盘存储的操作,直接添加磁盘,或动态在原有的分区上扩容,所使用的方案大致有如下:1.基于lvm格式 在线动态扩容2.非lvm格式扩容接下来我们就结合生产常用两种扩容方案及实践 二.LVM在线动态扩容 1.lvm原理概述 这里引用鸟哥说明LVM 的重点在於『可以弹性的调整 filesystem 的容量!』而并非在於效能与数据保全上面. 需要文件的读写效能或者是数据的可靠性,请参考前面的 RAID 小节. LVM 可以整合

Hadoop集群动态扩容、缩容

一. Hadoop集群动态扩容.缩容 随着公司业务的增长,数据量越来越大,原有的datanode节点的容量已经不能满足存储数据的需求,需要在原有集群基础上动态添加新的数据节点.也就是俗称的动态扩容. 有时候旧的服务器需要进行退役更换,暂停服务,可能就需要在当下的集群中停止某些机器上hadoop的服务,俗称动态缩容. 1. 动态扩容 1.1. 基础准备 在基础准备部分,主要是设置hadoop运行的系统环境 修改新机器系统hostname(通过/etc/sysconfig/network进行修改)

如何设计动态扩容缩容的分库分表方案?

面试官:如何来设计动态扩容的分库分表方案?面试官心理剖析:这个问题主要是看看你们公司设计的分库分表设计方案怎么样的?你知不知道动态扩容的方案? 回答: 背景说明:如果你们公司之前已经做了分库分表,你们当时分了 4 个库,每个库 4 张表:公司业务发展的很好,现在的数据库已经开始吃力了,不能满足快速发展的业务量了,需要进行扩容. 1)停机扩容 这个方案跟单库迁移方案是一样的,就是停服进行数据迁移,不过现在的数据迁移比之前的单库迁移要复杂的多,还有数据量也是之前的好几倍,单库的数据量可能就几千万,但

lvm 动态扩容

1,扩展VMWare中的硬盘空间关掉虚拟机,增加硬盘空间2,分区格式化fdisk -l 可以看到/dev/sda 的空间变大了#fdisk /dev/sdap/n/3/回车/t/3/8e/w/qp 查看现有分区情况,如是新硬盘,就没有分区n 新加一个分区3 新硬盘就是1,已分区硬盘就是空闲的3回车 设定新分区的起始扇区,如果原硬盘就是LVM管理,那么离线扩容的空间自动添加在原硬盘最后的位置回车 设定新分区的结束扇区t 修改分区类型3 选择要修改的分区号8e 修改为LVM,它就是8ew 写入分区表

Docker容器硬盘动态扩容

扩容容器 docker容器默认的空间是10G,如果想指定默认容器的大小(在启动容器的时候指定),可以在docker配置文件里通过dm.basesize参数指定,比如 1 docker -d --storage-opt dm.basesize=20G 是指定默认的大小为20G,具体参数可以参考https://github.com/docker/docker/tree/master/daemon/graphdriver/devmapper 上面方法只是真的生成容器的时候进行的,并且修改后需要重启do