一、索引检查

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. H.264 Profile、Level、Encoder三张简图 (fps = AVCodecContext->time_base.den / AVCodecContext->time_base.num)

    H.264 Profiles Profiles are sets of capabilities. If your black box only supports the Baseline profi ...

  2. HDU4349--Xiao Ming's Hope(数论)

    输入一个n(1<=n<=108),求C(n,0),C(n,1),C(n,2)...C(n,n)有多少个奇数. Lacus定理 http://blog.csdn.net/acm_cxlove ...

  3. POJ2923--Relocation(01背包+状压dp)

    果然对状压DP,我根本就不懂=.= /************************************************** Problem: 2923 User: G_lory Mem ...

  4. Sublime Text 2 使用心得

    一. 前言 作为一个前端,有一款好的开发利器是必不可少的,editplus.notepad++都是不错的工具,体积轻巧,启动迅速(dw太浮肿了).最近,又有一款新的编辑器诞生,席卷前端界,惹得无数喜爱 ...

  5. 打开链接(C# / 默认浏览器)

    System.Diagnostics.Process.Start("http://www.baidu.com/");

  6. 权限管理(java+struts2(自定义标签)实现)--------->全代码演示

    地址:http://blog.chinaunix.net/uid-24343152-id-3673026.html 最近由于项目不是很紧所以总结了之前做了n遍的权限管理功能.以便之后系统copy之用. ...

  7. JavaScript的递归之更多例子

    更多例子 第二个递归的例子是求两个自然数的最大公约数(有没有回到令人怀念的中学时代).下面的程序用的是经典的辗转相除法. //greatest common divisor //假定a.b都是正整数 ...

  8. hdu4486 Pen Counts(水题)

    Pen Counts Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  9. Sql Server2005 Transact-SQL 窗口函数(OVER)

    1.简介: SQL Server 2005中的窗口函数帮助你迅速查看不同级别的聚合,通过它可以非常方便地累计总数.移动平均值.以及执行其它计算.窗口函数功能非常强大,使用起来也十分容易.可以使用这个技 ...

  10. 【转】oracle创建表空间

    原文:http://www.cnblogs.com/netsql/articles/1745978.html 注意点: 1.如果在PL/SQL 等工具里打开的话,直接修改下面的代码中[斜体加粗部分]执 ...