一、索引检查

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. 有关window.location 对象

    之前在有关location的使用就一种方式给他的href赋值使页面跳转.今天看项目中的代码看到了一个不一样的用法 window.location.href = 'http://' + document ...

  2. eclipse老是卡在Refreshing workspace

    最近老是遇到eclipse没有响应,参考网络上的相关帖子做了些优化,但问题依旧. 后来发现出现这个问题一般都是eclipse无响应,直接结束进程后导致的,看了下eclipse日志,果真如此. !SES ...

  3. HBase 学习笔记---守护进程及内存调优

    1.HMaster           HMaster的任务前面已经说过了,两个大方向:一.管理Hbase Table的 DDL操作 二.region的分配工作,任务不是很艰巨,但是如果采用默认自动s ...

  4. C# 导入文件日期格式(dd/MM/yyyy)

      最近项目导入数据日期变成了这样 这是因为转换成了美国时间 美国时间格式:MM/dd/yyyy 英国时间格式:dd/MM/yyyy     避免导入出现这个问题的话就需要将excel文档单元格样式设 ...

  5. JAVA自动生成正则表达式工具类

    经过很久的努力,终于完成了JAVA自动生成正则表达式工具类.还记得之前需要正则,老是从网上找吗?找了想修改也不会修改.现在不用再为此烦恼了,使用此生成类轻松搞定所有正则表达式.赶快在同事面前炫一下吧. ...

  6. mysql 源代码学习 博客 [lock..]

    http://blog.chinaunix.net/uid/28364803/cid-168292-list-1.html

  7. schedule() 和 scheduleAtFixedRate() 的区别--转载

    1.  schedule() ,2个参数方法:在执行任务时,如果指定的计划执行时间scheduledExecutionTime <= systemCurrentTime,则task会被立即执行. ...

  8. EL表达式读取数据(在Map,javaBean,List)

    <%@page import="cn.hncu.domain.User"%><!--这里是进行导包--><%@ page language=" ...

  9. spinner中的onNothingSelected方法到底什么时候调用?

    这个东东大家在开发中可能不太能用到,所以总是容易被忽略,由于工作原因,我最近琢磨了一下onNothingSelected方法的调用时机问题,其实很简单,只要我们稍微看一下源码就明白了: /** * C ...

  10. Android(java)学习笔记144:Android音视频录制类MediaRecorder用法举例

    Android语音录制可以通过MediaRecorder和AudioRecorder.MediaRecorder本来是多媒体录制控件,可以同时录制视频和语音,当不指定视频源时就只录制语音(默认录制语言 ...