ArrayList(源码分析概述)

/*
 * ### ArrayList的使用和实现
 * List list=new ArrayList();
 * 
 * public class ArrayList<E> 
 *         extends AbstractList<E> 通过继承抽象类可以共享所有公共方法
        implements List<E>, 实现List接口 
        RandomAccess,  实现随机访问接口
        Cloneable, 实现克隆接口
        java.io.Serializable  实现序列化接口
    
        
 *  transient Object[] elementData;  真是存放数据的数组
 *  
 *  private int size; 当前集合中存储的元素个数 
 *  
 *  构造器
 *       public ArrayList() {
 *          //针对存储数据的数组进行初始化操作
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
            //常量定义为DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};空数组
            //如果使用无参构造器,则不会直接创建数组,而是采用空数组,当第一次添加元素时才创建数组
            //使用无参构造器时ArrayList会构建一个空数组用于未来存放数据,这里是一种对内存消耗的优化处理
        }
        
        
        带参构造器   List list=new ArrayList(18) 
            18就是初始化容积,这里的初始化参数值必须为[0,int的最大值)
        
    public ArrayList(int initialCapacity) {  //参数为初始化容积
        if (initialCapacity > 0) {  如果初始化容积大于0,则按照指定的容积创建数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;  空数组,长度为0的数组
        } else {  //初始化容积值小于0则报异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

    add方法的定义[重点]
    
    protected transient int modCount = 0;
    
    public boolean add(E e) {
        modCount++; 用于统计当前集合对象的修改次数   
        add(e, elementData, size);  参数1:新增元素;参数2:存储数据的数组;参数3:当前集合中存储的元素个数
        return true;   返回true表示添加成功
    }
    
    private void add(E e, Object[] elementData, int s) {
        //如果数组已经存满了,则首先进行扩容处理
        if (s == elementData.length)  判断当前集合中存储的元素个数是否和数组长度相等
            elementData = grow();   如果相等则进行扩容处理
        elementData[s] = e; 在数组的指定位置存储元素
        size = s + 1;  集合中所存储的元素个数
    }
    
    private Object[] grow() {  //扩容处理方法
        return grow(size + 1);  //继续调用其它扩容方法,参数为 当前存储个元素个数+1---最小容积
    }

    private Object[] grow(int minCapacity) {
         //调用Arrays工具类中的方法进行数组的拷贝,同时调用newCapacity方法计算所需要的新容积值
        return elementData = Arrays.copyOf(elementData,newCapacity(minCapacity));  
    }
    
    
    private static final int DEFAULT_CAPACITY = 10;
    
    private int newCapacity(int minCapacity) {  计算所需要的新容积值
        int oldCapacity = elementData.length;  计算原来数组的长度
        //计算新容积值
        int newCapacity = oldCapacity + (oldCapacity >> 1); 相当于是老容积增加50%,这就是扩容比
        //计算出的新容积值是否满足所需要的最小容积值
        if (newCapacity - minCapacity <= 0) {
            //如果存储数据的数组为初始化时的空数组,则计算默认初始化容积10和所需要最小容积值则最大值
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // OOM内存溢出
                throw new OutOfMemoryError();
            return minCapacity;
        }
        //如果计算出的新容积值大于所需要的最小容积值
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
                //如果计算出的新容积值小于最大允许的容积值,则返回计算出的新容积
                     (minCapacity > MAX_ARRAY_SIZE)
                        ? Integer.MAX_VALUE
                        : MAX_ARRAY_SIZE;
           //如果计算出的新容积值大于最大允许的容积值并且minCapacity大于MAX_ARRAY_SIZE,则返回最大整数值,否则返回最大允许的容积值
    }
    
    //数组长度的上限值
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
 * 
 *  
 *  数组的拷贝操纵定义在Arrays工具类中,copyof会新建一个指定长度newLength的数组,并且将原始数组中的所有元素拷贝到新数组中,同时返回新创建的数组
 *  参数1是原始数组,参数2为所需要的新长度
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
    
    
     * The maximum size of array to allocate (unless necessary).
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     * 
     * 要分配的最大数组大小(除非必要)。一些虚拟机在数组中保留一些头
     * 字。尝试分配较大的阵列可能会导致:OutOfMemoryError请求的数组大小超过VM限制
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
    由于在不同的平台上,受到平台的影响导致能够为数组分配的实际最大数值并非
    为Integer.MAX_VALUE(2,147,483,647),而是与这个值相接近的数值。因此,
    作者减8实际上是因为不想让你创建的数组在扩容时计算的新容量值等于或过于
    接近最大值不能被平台分配出来而报出OOM错误。
 
 *结论:
 * - 如果直接初始ArrayList,则采用延迟初始化处理【初始化数组长度为0】的
 * 方式以节约内存,当添加数据时,才进行数组的初始化,默认初始化容积为10
 * - 添加数据时当存储数据的数组长度不足时,数组会自动变长,变长的比例为
 * 原容积的50%
 * 
 * 
 * 
 * 删除元素
 *  public E remove(int index) {
        Objects.checkIndex(index, size);   //检查所需要删除位置下标参数是否在合理的范围内[0,size-1],如果超出范围则报异常
        final Object[] es = elementData;  获取所存储的所有元素数组
        E oldValue = (E) es[index];  获取指定位置上的元素
        fastRemove(es, index);   利用System.arrayCopy的方法使用覆盖的方式删除指定位置上的元素
        return oldValue;  返回删除掉的元素
        
        //没有缩容处理
    }
    
    private void fastRemove(Object[] es, int i) {
        modCount++;   修改次数+1
        final int newSize;  
        if ((newSize = size - 1) > i) //size - 1就是可以删除元素的最大下标
            System.arraycopy(es, i + 1, es, i, newSize - i); 通过数组拷贝的方式覆盖掉指定位置的元素
        es[size = newSize] = null; //最后位置赋null值
        
        {1,2,3,4,5} 执行arrayCopy方法需要覆盖下标为2的元素{1,2,4,5,5}
    }
    
    结论:
    - 使用数组元素移动的方式实现元素的删除。注意:这里没有变小容积
    - 修改元素个数时会有modCount的修改--快速失败
    
    
    get方法的实现
        public E get(int index) {
        Objects.checkIndex(index, size);针对索引下标进行合法性验证
        return elementData(index);
    }
    
    E elementData(int index) {
        return (E) elementData[index];
    }
    
    迭代器的实现类中定义【内部类】
    private void checkForComodification() {
        if (modCount != expectedModCount) { 
            throw new ConcurrentModificationException(); 并发修改异常
        }
    }
    
    结论:首先要求index应该在[0,size-1]的范围内,否则异常
          如果index正确则按照下标从数组中获取元素

            如果多个线程同时操作一个ArrayList,可以通过ConcurrentModificationException解决修改出现的线程安全问题
 */

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340