ArrayList源码分析

1、java.util.ArrayList<E> : List 接口的大小可变数组的实现类

  • ArrayList 内部基于 数组 存储 各个元素。
  • 所谓大小可变数组,是指当 数组容量不足以存放新的元素时,创建新数组,并将原数组中的内容复制过来。

2、ArrayList底层实现原理

  • 构造方法源码分析

    //对象数组:ArrayList的底层数据结构,transient表示该字段不进行序列化操作
    transient Object[] elementData;
    //实例化一个空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //实例化一个空数组
    private static final Object[] EMPTY_ELEMENTDATA = {}; /**
    *指定初始容量的有参构造
    */
    public ArrayList(int initialCapacity) {
    //如果初始容量大于0就,对象数组就指向到一个新的数组,大小为所指定的大小
    if (initialCapacity > 0) {
    this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
    //如果大于0就指向一个空数组
    this.elementData = EMPTY_ELEMENTDATA;
    } else {
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    }
    }
    /**
    * 无参构造
    */
    public ArrayList() {
    //对象数组就指向到一个空数组
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    ArrayList基于数组实现,构造方法有有参构造和无参构造如果指定了初始容量且大于0就将对象数组指定到一个新的数组,大小为所指定的大小。如果调用无参构造就将对象数组指定到一个空的数组。

  • 添加方法源码分析
    //elementData中已存放的元素的个数,注意:不是elementData的容量
    private int size;
    //elementData的默认容量为10
    private static final int DEFAULT_CAPACITY = 10;
    //对象数组:ArrayList的底层数据结构,transient表示该字段不进行序列化操作
    transient Object[] elementData;
    //实例化一个空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //实例化一个空数组
    private static final Object[] EMPTY_ELEMENTDATA = {}; protected transient int modCount = 0; @Native public static final int MAX_VALUE = 0x7fffffff; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; /**
    * 向elementData末尾中添加元素
    */
    public boolean add(E e) {
    //确保对象数组elementData有足够的容量,可以将新加入的元素e加进去
    ensureCapacityInternal(size + 1);
    //加入新元素e,size加1
    elementData[size++] = e;
    return true;
    } // minCapacity = seize+1,即表示执行完添加操作后,数组中的元素个数
    private void ensureCapacityInternal(int minCapacity) {
    //判断是否是空数组
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    //用最小容量和10进行比较,取最大值赋值给最小容量
    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    } ensureExplicitCapacity(minCapacity);
    } /**
    *确保数组的容量足够存放新加入的元素,若不够,要扩容
    */
    private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    //如果数组个数minCapacity (size+1)大于数组长度就需要进行扩容
    if (minCapacity - elementData.length > 0)
    grow(minCapacity);
    } private void grow(int minCapacity) {
    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);
    // 根据新的容量,将数组拷贝到新的数组并赋值给数组
    elementData = Arrays.copyOf(elementData, newCapacity);
    } private static int hugeCapacity(int minCapacity) {
    // 如果数组个数小于0抛出OutOfMemoryError异常
    if (minCapacity < 0) // overflow
    throw new OutOfMemoryError();
    // 如果最数组个数大于最大容量 就返回最大值,否则返回最大容量
    return (minCapacity > MAX_ARRAY_SIZE) ?
    Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
    }
    /**
    * 向elementData指定位置添加元素
    */
    public void add(int index, E element) {
    //指定位置检查
    rangeCheckForAdd(index);
    // 扩容检查
    ensureCapacityInternal(size + 1); // Increments modCount!!
    //通过拷贝使数组内位置为 index 到 (size-1)的元素往后移动一位
    System.arraycopy(elementData, index, elementData, index + 1,
    size - index);
    // 找到位置添加元素
    elementData[index] = element;
    // 元素个数加一
    size++;
    }
    // 判断指定位置是否超出数组个数
    private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    /**
    * @param src 原数组.
    * @param srcPos 原数组的起始位置.
    * @param dest 目标数组.
    * @param destPos 目标数组的起始位置.
    * @param length 拷贝长度.
    */
    public static native void arraycopy(Object src, int srcPos,
    Object dest, int destPos,
    int length);

    ArrayList在无参的add方法中,每次插入新的元素时,先判断是否需要扩容,判断数组是否为空,为空则建默认的容量10赋值给minCapacity,判断是否要扩容,第一次添加,数组的size是10,之后添加元素时,在ensureExplicitCapacity方法中判断数组元素个数即size+1(形参minCapacity)是否超过数组长度,超过则需要进行扩容操作,扩容是将旧的容量扩大到1.5倍,然后将数组拷贝到新的数组完成扩容操作。最后将元素添加,并size+1。ArrayList在指定位置添加元素时,是先检查指定位置是否在数组范围内,即数组中元素个数是1,则index得小于或者低于1。然后通过拷贝使数组内位置为 index 到 (size-1)的元素往后移动一位,腾出位置之后放入元素,数组个数加一。

  • 删除方法源码分析
    public E remove(int index) {
    //指定位置检查
    rangeCheck(index);
    modCount++;
    //保留要删除的值
    E oldValue = elementData(index);
    //要移动元素个数
    int numMoved = size - index - 1;
    if (numMoved > 0)
    //通过拷贝使数组内位置为 index+1到 (size-1)的元素往前移动一位
    System.arraycopy(elementData, index+1, elementData, index,
    numMoved);
    //清除末尾元素让GC回收
    elementData[--size] = null; // clear to let GC do its work
    //返回删除的值
    return oldValue;
    }

    删除指定位置的元素,先对index进行检查,在将要删除的值保留,计算出需要移动元素个数,再通过拷贝使数组内位置为 index+1到 (size-1)的元素往前移动一位,最后将末尾元素清理让GC回收返回删除值。由于List接口继承了Collection,因此ArrayList还有一个来自Collection接口定义的删除对象boolean remove( Object o ) ,而ArrayList自定义的remove方法是T remove(int index)删除的是下标位置的对象并返回值。

参考博客链接

https://my.oschina.net/90888/blog/1625416

转载请于明显处标明出处

https://www.cnblogs.com/AmyZheng/p/9427140.html

ArrayList的底层实现原理的更多相关文章

  1. ArrayList 底层实现原理

    ArrayList的底层实现原理 1, 属性:private static final int DEFAULT_CAPACITY = 10; private static final Object [ ...

  2. HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别(转)

    HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别 文章来源:http://www.cnblogs.com/beatIteWeNerverGiveU ...

  3. JAVA ArrayList集合底层源码分析

    目录 ArrayList集合 一.ArrayList的注意事项 二. ArrayList 的底层操作机制源码分析(重点,难点.) 1.JDK8.0 2.JDK11.0 ArrayList集合 一.Ar ...

  4. PHP底层工作原理

    最近搭建服务器,突然感觉lamp之间到底是怎么工作的,或者是怎么联系起来?平时只是写程序,重来没有思考过他们之间的工作原理: PHP底层工作原理 图1 php结构 从图上可以看出,php从下到上是一个 ...

  5. javaSE基础之 ArrayList的底层简单实现

    最近就是想扒一扒存在硬盘里面的学习资料(突然想到什么),把以前写过的一些东西整理一下分享出来. 这边是ArrayList 的简单实现,当然只实现了部分方法 package com.yck.collec ...

  6. Java并发之底层实现原理学习笔记

    本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...

  7. spirng底层实现原理

    什么是框架?框架解决的是什么问题? 编程有一个准则,Don't Repeat Yourself(不要重复你的代码),所以我们会将重复的代码抽取出来,封装到方法中:如果封装的方法过多,将将这些方法封装成 ...

  8. iOS weak底层实现原理

    今年年底做了很多决定,离开工作三年的深圳,来到了上海,发现深圳和上海在苹果这方面还是差距有点大的,上海的市场8成使用swift编程,而深圳8成的使用OC,这点还是比较让准备来上海打拼的苹果工程师有点小 ...

  9. ArrayList的实现及原理

    ArrayList ArrayList是最常见以及每个Java开发者最熟悉的集合类了,顾名思义,ArrayList就是一个以数组形式实现的集合,以一张表格来看一下ArrayList里面有哪些基本的元素 ...

随机推荐

  1. Servlet文件上传下载

    今天我们来学习Servlet文件上传下载 Servlet文件上传主要是使用了ServletInputStream读取流的方法,其读取方法与普通的文件流相同. 一.文件上传相关原理 第一步,构建一个up ...

  2. mysql学习笔记(1)

    以下笔记并不系统,只是针对遇到的问题和特别的点记录一下: 数据类型: 1.mysql小数存储数据类型 有float double decimal ,前两个不属于精确类型,不推荐使用,一般生产库亦不会使 ...

  3. Linux - 文件时间戳

    概述 简介 linux 文件时间戳 背景 最近感觉很消极的样子 心情不好加不知道写啥 随便水一水 能水的就那么多, 水一次, 少一次 环境 os centos7 1. 时间戳 概述 简述 时间戳 li ...

  4. 【游戏体验】Sift Heads World Act 1(暗杀行动1)

    >>>点此处可试玩无敌版<<< 注意,本游戏含有少量暴力元素,13岁以下的儿童切勿尝试本款游戏 非常良心的火柴人游戏,值得一玩 个人测评 游戏性 8/10 音乐 9 ...

  5. webpack配置的说明

    {devtool: 'source-map',//要启用source-map需加上此配置项,同时css或less的loader要加上参数?sourceMap,js的loader不用加 entry: e ...

  6. JarvisOJ - Writeup(5.31更新)

    此篇用来记录我在jarivsOJ上的一些题解,只给解题思路,不放flag Misc 0x01 You Need Python(300) 题目有两个文件,一个py文件,另一个是经过编码的key 文件ke ...

  7. css动画效果之transition(动画效果属性)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. 让ul li 或者table 进行循环往上滚屏

    转载:https://blog.csdn.net/u012138137/article/details/80729789 <div style="display:inline" ...

  9. 把jar包部署为linux服务

    一直未配置成功,直到放弃后reboot了下,才直到错的不是自己的配置,而是自己不懂 1.在touch /etc/rc.d/init.d/tl_c_cons_service(创建新文件) 2.vi /e ...

  10. Windows10_64位下upload-labs靶场搭建+phpstudy_v8.1安装教程

     之前介绍了Windows10_64位搭建WampServer的教程,这一次再来水一篇phpstudy的搭建教程.哈哈哈.     顺便安装一下upload-labs,搭着玩玩~         操作 ...