一、索引检查

1)在指定位置插入元素时,第一步都需要检查输入的指定位置是否合法

public void add(int index, E element)
{
    rangeCheckForAdd(index);
    ...
}

==>

private void rangeCheckForAdd(int index)
{
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

分析:rangeCheckForAdd方法用于检查index是否越界。如果该index大于ArrayList元素个数或者小于0时,抛出索引越界异常。

其中outOfBoundsMsg方法,是用来展示IndexOutOfBoundsException detail message。

private String outOfBoundsMsg(int index)
{
    return "Index: " + index + ", Size: " + size;
}

2)在指定位置删除元素时,第一步也需要检查输入的索引号是否合法

public E remove(int index)
{
    rangeCheck(index);
    ...
}

==>

private void rangeCheck(int index)
{
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

分析:rangeCheck方法只需检测index索引是否超出ArrayList元素个数。

 
问题1:为什么添加元素和删除元素使用的索引检查方法不同呢?
 

二、确保容量

1)增加容量

public void add(int index, E element)
{
    rangeCheckForAdd(index);
    ensureCapacityInternal(size + 1);
    ...
}

==>

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

其中,minCapacity取与默认值之间的最大值。

ensureExplicitCapacity ==>
private void ensureExplicitCapacity(int minCapacity)
{
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
 
问题二:size与elementData.length之间的关系?
如果size==elementData.length,if (minCapacity - elementData.length > 0)就一直会成立。
分析:
  • size很好理解,就是ArrayList的长度大小,也可理解为ArrayList中已添加的元素个数,这里需要注意:ArrayList允许添加为null的元素,所以null也会占用一份空间。
  • elementData就是ArrayList的内部数组实现,它是一个一维对象数组。当通过add方法向ArrayList中添加元素时,这些元素就会被依次存储于elementData这个数组中。 elementData既然是数组,它必然拥有长度length,那么这个数组的长度是否等于ArrayList的size?

这里需要了解一些ArrayList的相关设计概念:

1、作为ArrayList的内部实现数组elementData具备一定的长度(初始值为10),此长度又被称为capacity(容量)。

2、而每一个ArrayList实例又具有一个size,此size是该实例的列表长度,这其中capacity永远等于数组elementData的长度,并永远大于等于列表size。
所以capacity可以理解为ArrayList的容纳能力,而size可以理解为已使用的空间大小。用生活中的例子,鞋柜就像一个容器,容器的大小(capacity)都是不变的,而其中size会随着填充元素数量增加而增加,但最终只可能等于容器大小。

注意:elementData.length等于elementData.size(),但是ArrayList中的size是记录ArrayList元素的个数,而elementData是用来存放ArrayList的元素。或者说elementData相当于鞋柜容器,它的容器大小==elementData.length==elementData.size(),而ArrayList的size相当于鞋子双数。

参考:List实现之ArrayList
a)容量增长算法
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);
}
  1. 得到数组的旧容量,然后进行oldCapacity + (oldCapacity >> 1),将oldCapacity 右移一位,其效果相当于oldCapacity /2,我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
  2. 然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
  3. 再检查新容量是否超出了ArrayList所定义的最大容量,若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为ArrayList定义的最大容量,否则,新容量大小则为 minCapacity。 (在判断容量是否超过MAX_ARRAY_SIZE的值,MAX_ARRAY_SIZE值为Integer.MAX_VALUE - 8,比int的最大值小8,不知道为什设计,可能方便判断吧。如果已经超过,调用hugeCapacity方法检查容量的int值是不是已经溢出。一般很少用到int最大值的情况,那么多数据也不会用ArrayList来做容器了,估计这辈子没机会见到hugeCapacity运行一次了。)
  4. 最后确定了新的容量,就使用Arrays.copyOf方法来生成新的数组,copyOf也已经完成了将就的数据拷贝到新数组的工作。(Arrays.copyof(···)与System.arraycopy(···)区别

需要注意的是,容量拓展,是创建一个新的数组,然后将旧数组上的数组copy到新数组,这是一个很大的消耗,所以在我们使用ArrayList时,最好能预计数据的大小,在第一次创建时就申请够内存。

参考:ArrayList实现原理以及其在jdk1.6和jdk1.7的实现区别

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

2)缩小容量

ArrayList还给我们提供了将底层数组的容量调整为当前列表保存的实际元素的大小的功能。它可以通过trimToSize方法来实现。
public void trimToSize()
{
    modCount++;
    if (size < elementData.length)
    {
        elementData = (size == 0)
        ? EMPTY_ELEMENTDATA
        : Arrays.copyOf(elementData, size);
    }
}
 

专题一、ArrayList增删操作技术细节详解的更多相关文章

  1. SQL Server 表的管理_关于数据增删查改的操作的详解(案例代码)

    SQL Server 表的管理_关于数据增删查改的操作的详解(案例代码)-DML 1.SQL INSERT INTO 语句(在表中插入) INSERT INTO 语句用于向表中插入新记录. SQL I ...

  2. SQL Server 表的管理_关于表的操作增删查改的操作的详解(案例代码)

    SQL Server 表的管理_关于表的操作增删查改的操作的详解(案例代码) 概述: 表由行和列组成,每个表都必须有个表名. SQL CREATE TABLE 语法 CREATE TABLE tabl ...

  3. Java8 Stream终端操作使用详解

    话不多说,自己挖的坑自己要填完,今天就给大家讲完Java8中Stream的终端操作使用详解.Stream流的终端操作主要有以下几种,我们来一一讲解. forEach() forEachOrdered( ...

  4. 005-Scala数组操作实战详解

    005-Scala数组操作实战详解 Worksheet的使用 交互式命令执行平台 记得每次要保存才会出相应的结果 数组的基本操作 数组的下标是从0开始和Tuple不同 缓冲数组ArrayBuffer( ...

  5. ASP.NET 操作Cookie详解 增加,修改,删除

    ASP.NET 操作Cookie详解 增加,修改,删除 Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密).定义于RFC2109.它 ...

  6. 在telnet下操作memcache详解(操作命令详解)

    这篇文章主要介绍了在telnet下操作memcache详解,telnet下的memcache操作命令详解,需要的朋友可以参考下 在定位问题.测试等时候经常需要对memcache的数据进行一些操作,但是 ...

  7. Spring Data操作Redis详解

    Spring Data操作Redis详解 Redis是一种NOSQL数据库,Key-Value形式对数据进行存储,其中数据可以以内存形式存在,也可以持久化到文件系统.Spring data对Redis ...

  8. net平台下c#操作ElasticSearch详解

    net平台下c#操作ElasticSearch详解 ElasticSearch系列学习 ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense Elasti ...

  9. 安装MACOS操作步骤详解

    安装MACOS操作步骤详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 关于安装MAC的操作系统其实大家都知道可以让客服帮忙提供软件上的支持,而且苹果客服都很有礼貌呢,而且非常的 ...

随机推荐

  1. POJ1502: MPI Maelstrom

    红果果的dijstra算法应用,这里采用邻接表存储图 小插曲:while(scanf("%d",&n))提交时内存超限,改成while(scanf("%d&quo ...

  2. json.net json转换神器

    json.nethttps://json.codeplex.com/ api documenthttp://james.newtonking.com/json/help/index.html#

  3. tail -f logfile.log 一直监控某个文件,若该文件有改动,立即在屏幕上输出

    tail -f logfile.log 可以一直监控某个文件,只要文件有改动,就立即在屏幕上输出

  4. mysql常见优化,更多mysql,Redis,memcached等文章

    mysql常见优化 http://www.cnblogs.com/ggjucheng/archive/2012/11/07/2758058.html 更多mysql,Redis,memcached等文 ...

  5. SQL SERVER全面优化

    今天我们从语句的一些优化写法及一些简单优化方法做一个介绍.这对于很多开发人员来说还是很有用的!为了方便阅读给出前文链接: SQL SERVER全面优化-------Expert for SQL Ser ...

  6. Java开发中常见的危险信号(中)

    本文来源于我在InfoQ中文站原创的文章,原文地址是:http://www.infoq.com/cn/news/2013/12/common-red-flags-in-java-1 Dustin Ma ...

  7. List 排序

    1:sort 这种实现是用朗姆达表达式实现.Comparison<T> 委托详见 http://msdn.microsoft.com/zh-cn/library/tfakywbh.aspx ...

  8. OllyDbg 使用笔记 (二)

    OllyDbg 使用笔记 (二) 參考 书:<加密与解密> 视频:小甲鱼 解密系列 视频 TraceMe.exe下载地址:http://pan.baidu.com/s/1c0s2twO T ...

  9. C# Attribute (上)——Attribute初体验

      原始出处 .作者信息和本声明.否则将追究法律责任.http://liutiemeng.blog.51cto.com/120361/29201 小序:          注意:本次小序颇长而且没什么 ...

  10. 第一个html程序

    <html><head><title> 表单</title> </head><body><form action=&quo ...