【集合框架】JDK1.8源码分析之ArrayList详解(一)

一. 从ArrayList字表面推测

ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,List的中文意思是列表。从ArrayList字表面推测,ArrayList类是否有数组和列表的特征?那么,这些特征这在ArrayList类中又是怎么体现的?

ArrayList源码分析

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

从上面的代码可知道,以下几点

  1. 继承了AbstractList类,实现了List,意味着ArrayList是一个数组队列,提供了诸如增删改查、遍历等功能。
  2. 实现了RandomAccess接口,意味着ArrayList提供了随机访问的功能。RandomAccess接口在Java中是用来被List实现,用 来提供快速访问功能的。在ArrayList中,即我们可以通过元素的序号快速获取元素对象。
  3. 实现了Cloneable接口,意味着ArrayList实现了clone()函数,能被克隆。
  4. 实现了java.io.Serializable接口,意味着ArrayList能够通过序列化进行传输。

    ArrayList的属性

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
  • DEFAULT_CAPACITY :默认容量大小为10。
  • EMPTY_ELEMENTDATA :用于Constructs有参数传入,如果参数等于零,则使用该属性。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA :用于Constructs无参数传入默认大小为10(具体运用于add中),使用使用该属性。
  • size :当前容量大小

## 构造分析
### 有参数构造Method
传入initialCapacity参数,如果参数大于零,创建一个大小为initialCapacity的数组
/** * Constructs an empty list with the specified initial capacity. * * @param initialCapacity the initial capacity of the list * @throws IllegalArgumentException if the specified initial capacity * is negative */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { //new一个大小为initialCapacity的数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }

无参数构造Method

里面只有一个操作就是把 elementData 设置为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 这个空数组。

// 无参的构造函数,传入一个空数组  这时候会创建一个大小为10的数组,具体操作在 add 中
 public ArrayList() {
 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
 }

增删查改Method

add Method

这个方法首先调用了 ensureCapacityInternal() 这个方法里面就判断了当前的 elementData 是否等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 如果是的话,就把数组的大小设置为 10 然后进行扩容操作,这里刚好解释了为什么采用无参构造的List 的大小是 10 ,这里扩容操作调用的方法是 ensureExplicitCapacity 里面就干了一件事如果用户指定的大小 大于当前长度就扩容,扩容的方法采用了 Arrays.copy 方法,这个方法实现原理是 new 出一个新的数组,然后调用 System.arraycopy 拷贝数组,最后返回新的数组。

public boolean add(E e) {
 // 当调用了无参构造,设置大小为10
 ensureCapacityInternal(size + 1);  // Increments modCount
 elementData[size++] = e;
 return true;
}  

private void ensureCapacityInternal(int minCapacity) {
 // 如果当前数组是默认空数组就设置为 10和 size+1中的最小值
 // 这也就是说为什么说无参构造 new 的数组大小是 10
 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
 }  

 ensureExplicitCapacity(minCapacity);
}  

private void ensureExplicitCapacity(int minCapacity) {
 modCount++;  

 // 若用户指定的最小容量 > 最小扩充容量,则以用户指定的为准,否则还是 10
 if (minCapacity - elementData.length > 0)
 grow(minCapacity);
}  

private void grow(int minCapacity) {
 // overflow-conscious code
 int oldCapacity = elementData.length;
 // 1.5倍增长
 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);
}

Remove Method

通过index知道位置,然后index位置的值被index+1位置的值往前覆盖,覆盖的次数为size - index - 1

/**
     * Removes the element at the specified position in this list.
     * Shifts any subsequent elements to the left (subtracts one from their
     * indices).
     *
     * @param index the index of the element to be removed
     * @return the element that was removed from the list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

get Method

通过index 来访问元素

public E get(int index) {
            //检测范围
            rangeCheck(index);
            checkForComodification();
            return ArrayList.this.elementData(offset + index);
        }
 

set Method

通过index知道要修改的元素并替换了

  public E set(int index, E e) {
            rangeCheck(index);
            checkForComodification();
            E oldValue = ArrayList.this.elementData(offset + index);
            ArrayList.this.elementData[offset + index] = e;
           

【集合框架】JDK1.8源码分析之ArrayList详解(一)的更多相关文章

  1. nginx源码分析线程池详解

    nginx源码分析线程池详解 一.前言     nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...

  2. 【集合框架】JDK1.8源码分析之ArrayList(六)

    一.前言 分析了Map中主要的类之后,下面我们来分析Collection下面几种常见的类,如ArrayList.LinkedList.HashSet.TreeSet等.下面通过JDK源码来一起分析Ar ...

  3. vuex 源码分析(六) 辅助函数 详解

    对于state.getter.mutation.action来说,如果每次使用的时候都用this.$store.state.this.$store.getter等引用,会比较麻烦,代码也重复和冗余,我 ...

  4. vuex 源码分析(五) action 详解

    action类似于mutation,不同的是Action提交的是mutation,而不是直接变更状态,而且action里可以包含任意异步操作,每个mutation的参数1是一个对象,可以包含如下六个属 ...

  5. Golang源码分析之目录详解

    开源项目「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目 ...

  6. Java 容器源码分析之集合类详解

    集合类说明及区别 Collection ├List │├LinkedList │├ArrayList │└Vector │ └Stack └Set Map ├Hashtable ├HashMap └W ...

  7. Tomcat源码分析 | 一文详解生命周期机制Lifecycle

    目录 什么是Lifecycle? Lifecycle方法 LifecycleBase 增加.删除和获取监听器 init() start() stop() destroy() 模板方法 总结 前言 To ...

  8. Cloudera Impala源码分析: SimpleScheduler调度策略详解包括作用、接口及实现等

    问题导读:1.Scheduler任务中Distributed Plan.Scan Range是什么?2.Scheduler基本接口有哪些?3.QuerySchedule这个类如何理解?4.Simple ...

  9. 【集合框架】JDK1.8源码分析之HashMap(一) 转载

    [集合框架]JDK1.8源码分析之HashMap(一)   一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大的优化 ...

随机推荐

  1. 什么是TensorBoard?

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 回顾前面: 从零开始学TensorFlow[01-搭 ...

  2. 一、Java 23 种设计模式简介

    一.23种设计模式分类: 二.设计模式的六大原则: 1.开闭原则(Open Close Principle):对扩展开放,对修改关闭.在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效 ...

  3. <pre>标签:\r\n换行符的保留

    mysql数据读库的\r\n换行符处理 这个问题是我在采集数据时发现的,采集网页的数据,大概有6千多条,采集的内容保留了最原始的\r\n和\n换行字符,但在mysql管理工具中(phpmyadmin和 ...

  4. 2018-09-15 Java源码英翻中库以及服务原型

    服务很简单, 只为演示这个库, 源码在: program-in-chinese/code_translator_service. 在Postman测试效果: 演示服务地址: 74.91.17.250: ...

  5. 2017-12-09 JavaScript实现ZLOGO子集: 测试用例

    续前文JavaScript实现ZLOGO子集: 前进+转向. 在添加新功能之前, 先添加测试用例, 以应对日益复杂的代码. 选择使用QUnit编写运行测试用例. 暂时对比较复杂和I/O无关的部分进行测 ...

  6. bat脚本自定义魔兽warIII运行分辨率,去黑边

    我们一般平时安装完WarIII后运行时的分辨率默认是800*600,导致有黑边的存在.所以我写了一个bat脚本来自定义WarIII的运行分辨率.需要以管理员身份运行. 下载链接: 链接:https:/ ...

  7. winfrom 图片等比例压缩

    效果图: 核心代码: /// <summary> /// 等比例缩放图片 /// </summary> /// <param name="bitmap" ...

  8. elasticsearch常用命令备注

    1.检查集群健康状态 curl 'localhost:9200/_cat/health?v' 2.检查节点健康状态 curl 'localhost:9200/_cat/nodes?v' 3.新增一条索 ...

  9. 9. VIM 系列 - YouCompleteMe 实现代码补全

    目录 环境准备 插件安装 环境准备 $ sudo apt install build-essential cmake python3-dev python-dev $ sudo apt install ...

  10. springboot的war和jar包

    本篇和大家分享的是通过maven对springboot中打war包和jar包:war通常来说生成后直接放到tomcat的webapps下面就行,tomcat配置自动解压war,而jar一般通过命令行部 ...