ArrayList源码阅读笔记

news2024/11/15 10:24:50

1. 基础知识

1.1 概念

  1. ArrayList是可以动态增长和缩减的索引序列,它是基于数组实现的List类。
  2. 该类封装了一个动态再分配的Object[]数组,每个对象都有一个capacity属性,表示它们所封装的Object[]数组长度,当向ArrayList中添加元素时,该属性会自动增加。
    如果向ArrayList中添加大量元素,可使用ensureCapacity()方法一次性增加capacity,可以减少增加重分配的次数提高性能。
  • ArrayList的用法和Vector相似,但Vector是一个较老的集合,具有很多缺点,不建议使用。
    ArrayList和Vector的区别是:ArrayList是线程不安全的,当多个线程访问同一个ArrayList集合时,程序需要手动保证该集合的同步性,而Vector是线程安全的。可以使用CopyOnWriteArrayList。

1.2 继承关系

在这里插入图片描述
由上图可知最根部就是实现了Collection接口,下面我们从ArrayList的角度来分析一下继承关系:
在这里插入图片描述
上面这张图可以很清晰的看出ArrayList实现了四个接口一个抽象类:继承AbstractList抽象类、实现了List、RandomAccess、Cloneable、Serializable接口。

  • 继承AbstractList, 实现了List。它是一个数组,提供了相关的增、删、修改、遍历等功能。
  • RandomAccess接口:这个是一个标记性接口,通过查看api文档,它的作用就是用来快速随机存取,有关效率的问题,在实现了该接口的话,那么使用普通的for循环来遍历,性能更高,例如ArrayList。
  • Cloneable接口:实现了该接口,就可以使用Object.Clone()方法了。
  • Serializable接口:实现该序列化接口,表明该类可以被序列化,什么是序列化?简单的说,就是能够从类变成字节流传输,然后还能从字节流变成原来的类。

1.3 特点

  1. ArrayList底层是一个动态扩容的数组结构,初始容量为10,每次容量不够的时候,扩容需要增加1.5倍的容量(当通过addAll()方法添加数据时有可能不是1.5被扩容);
  2. ArrayList允许存放重复数据,存储顺序按照元素的添加顺序,也允许Null;
  3. 底层使用Arrays.copyOf()函数进行扩容,每次扩容都会产生新的数组,和数组中内容的拷贝,会耗费性能,所以在多增删的操作情况可优先考虑LinkedList,如果多按下标存取元素情况下推荐使用ArrayList。
  4. ArrayList并不是一个线程安全的集合。如果集合的增删操作需要保证线程的安全性,可以考虑使用CopyOnWriteArrayList或者使用Collections.synchronizedList(List)函数返回一个线程安全的ArrayList类。

2. 源码阅读

2.1 类中的属性

   /**
   * 序列化时使用
   */
   private static final long serialVersionUID = 8683452581122892189L;

    /**
     * 集合的默认大小
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 用于空实例的共享空数组实例
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
    * 这也是一个空的数组实例,和EMPTY_ELEMENTDATA空数组相比是用于了解添加元素时数组膨胀多少
    */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * 存储ArrayList的元素的数组缓冲区。 ArrayList的容量是此数组缓冲区的长度。 
     * 添加第一个元素时,任何具有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList都将扩展为DEFAULT_CAPACITY。
     */
   transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 当前ArrayList大小
     */
    private int size;

EMPTY_ELEMENTDATADEFAULTCAPACITY_EMPTY_ELEMENTDATA的区分会在本文后续具体说明。

2.2 构造方法

ArrayList有三个构造方法

在这里插入图片描述

  1. 无参构造方法

    /**
      * Constructs an empty list with an initial capacity of ten. 这里就说明了默认会给10的大小,所以说一开始ArrayList的容量是10
      */
     public ArrayList() {
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     }
    

    ArrayList中存储的数据其实就是一个数组,这个数组就是elementDatam。此时初始容量是0。

  2. 有参数构造方法

      /**
      * 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) {
             this.elementData = new Object[initialCapacity];
         } else if (initialCapacity == 0) {
             this.elementData = EMPTY_ELEMENTDATA;
         } else {
             throw new IllegalArgumentException("Illegal Capacity: " +
                     initialCapacity);
         }
     }
    

    初始化集合大小创建 ArrayList 集合。当大于0时,给定多少那就创建多大的数组;当等于0时,创建一个空数组;当小于0时,抛出异常。

  3. 参数为集合的构造方法

      public ArrayList(Collection<? extends E> c) {
         elementData = c.toArray();
         if ((size = elementData.length) != 0) {
             // c.toArray might (incorrectly) not return Object[] (see 6260652)
             if (elementData.getClass() != Object[].class)
                 elementData = Arrays.copyOf(elementData, size, Object[].class);
         } else {
             // replace with empty array.
             this.elementData = EMPTY_ELEMENTDATA;
         }
      }
    

    这是将已有的集合复制到 ArrayList 集合中去。
    可以看到,不管调用哪个构造方法,都会初始化内部elementData。

2.3 添加元素

通过前面的字段属性和构造函数,我们知道 ArrayList 集合是由数组构成的,那么向 ArrayList 中添加元素,也就是向数组赋值。我们知道一个数组的声明是能确定大小的,而使用 ArrayList 时,好像是能添加任意多个元素,这就涉及到数组的扩容。

2.3.1 add()方法
  /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }


  /**
     * Inserts the specified element at the specified position in this
     * list. Shifts the element currently at that position (if any) and
     * any subsequent elements to the right (adds one to their indices).
     *
     * @param index index at which the specified element is to be inserted
     * @param element element to be inserted
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public void add(int index, E element) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

可以看到,不管是调用哪个构造方法,都会首先执行 ensureCapacityInternal(size + 1) 确认内部容量。

2.3.2 ensureCapacityInternal()
    private void ensureCapacityInternal(int minCapacity) {
        // 如果创建ArrayList时,使用的无参的构造方法,那么就取默认容量10和最小需要容量中的较大一个值
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

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

        // 如果minCapacity比当前容量大,就执行grow扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // 拿到当前容量
        int oldCapacity = elementData.length;
        // oldCapacity >> 1 意思是oldCapacity/2,所以新容量就是增加1/2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果心容量小于需要的最小扩容的容量,以需要的最小容量为准进行扩容
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 如果新容量大于允许的最大容量,则以Interger的最大值进行扩容
        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);
    }

    // 设置容器容量为最大容量
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

上面代码注释很清楚,不赘述。
此外ArrayList还有addAll(Collection<? extends E> c)、 addAll(int index, Collection<? extends E> c) 方法,类似。

2.4 移除元素——remove ()方法

     /**
     * 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) {
        // 判断是否越界 不过为啥不判断<0的情况呢
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        modCount++;
        E oldValue = (E) elementData[index];
        // 主要移动的元素数量,即 总长度-(下标 + 1)
        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;
    }



    /**
     * Removes the first occurrence of the specified element from this list,
     * if it is present.  If the list does not contain the element, it is
     * unchanged.  More formally, removes the element with the lowest index
     * <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i))) 
     * (if such an element exists).  Returns <tt>true</tt> if this list
     * contained the specified element (or equivalently, if this list
     * changed as a result of the call).
     *
     * @param o element to be removed from this list, if present
     * @return <tt>true</tt> if this list contained the specified element
     */
    public boolean remove(Object o) {
        if (o == null) {
            // 如果删除null数据,只会删除第一个null元素
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        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
    }

2.4 设置元素 —— set()方法

set()方法就是在指定位置改变一个元素的值

   /**
     * Replaces the element at the specified position in this list with
     * the specified element.
     *
     * @param index index of the element to replace
     * @param element element to be stored at the specified position
     * @return the element previously at the specified position
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E set(int index, E element) {
        //依然先判断是否越界,如果越界就抛出异常,你没有越界直接修改值,把旧值返回
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        E oldValue = (E) elementData[index];
        elementData[index] = element;
        return oldValue;
    }

2.5 其它方法


    /**
     * Returns the element at the specified position in this list.
     *
     * @param  index index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
     public E get(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

        return (E) elementData[index];
    }


    /**
     * Removes all of the elements from this list.  The list will
     * be empty after this call returns.
     */
     public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }


    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     */
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

    /**
     * Returns the index of the last occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the highest index <tt>i</tt> such that
     * <tt>(o==null&nbsp;?&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))</tt>,
     * or -1 if there is no such index.
     */
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

3. 总结

  1. ArrayList底层是一个动态扩容的数组,初始容量为10,每次容量不足时,扩容值当前的1.5倍容量。
  2. 增加(add)和删除(remove)操作会改变modCount,但是查找(get)和修改(set)不会修改。
  3. 从上面可以看出,增删都可能涉及数组拷贝,效率比较低,但是查找和修改时效率很高。
  4. 从上面可以看出,ArrayList对null元素是支持的,并且不会限制数量,也不会限制重复元素。
  5. 全文没见Synchronized关键字,也没有其它保证线程安全的操作,所以是线程不安全的,可以使用CopyOnWriteArrayList或者使Collections.synchronizedList(List)函数返回一个线程安全的ArrayList类来保证线程安全。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/59978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DAMOYOLO windows 单卡训练

最近达摩院放出了目前最能打的yolo算法&#xff0c;时间和精度都得到了提升 目前代码已经开源&#xff1a; 代码地址&#xff1a;GitHub - tinyvision/DAMO-YOLO: DAMO-YOLO: a fast and accurate object detection method with some new techs, including NAS backbones, effi…

详细的科技特长生路径和成长规划

2021年&#xff0c;教育部印发了《关于进一步加强和改进普通高等学校艺术类专业考试招生工作的指导意见》&#xff0c;在其中规范了艺术类专业的招生政策&#xff0c;包括艺术类考试和高水平艺术团考试&#xff0c;其中明确自2024年起&#xff0c;高校高水平艺术团不再从高校招…

今年双十二值得买的数码好物推荐!双十二数码产品抢购攻略

时间过得真快&#xff0c;一年一度的双十二年终最后的大促即将正式拉开序幕。此刻&#xff0c;不知道大家制定好购物计划了吗&#xff1f;如果你想入手数码好物&#xff0c;那么不妨看一下我刚刚整理的这份清单&#xff0c;涵盖了手机、平板、耳机等多个品类&#xff0c;相信总…

Centos Linux 7 查看网卡

Centos Linux 7 查看网卡 一. 二.

使用 Learner Lab - 使用 CloudWatch 进行排错,搭配 API Gateway 与 Lambda

使用 Learner Lab - 使用 CloudWatch 进行排错&#xff0c;搭配 API Gateway 与 Lambda AWS Academy Learner Lab 是提供一个帐号让学生可以自行使用 AWS 的服务&#xff0c;让学生可以在 100 USD的金额下&#xff0c;自行练习所要使用的 AWS 服务&#xff0c;如何进入 Learne…

设计模型之单例设计

前言 单例模式&#xff08;Singleton Pattern&#xff09;是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类&#xff0c;该类负责创建自己的对象&#xff0c;同时确保只有单个对象被…

3. Exchange 交换机的使用

二八佳人体似酥&#xff0c;腰间仗剑斩愚夫。虽然不见人头落&#xff0c;暗里教君骨髓枯。 在上一节中&#xff0c;我们创建了一个工作队列。我们假设的是工作队列背后&#xff0c;每个任务都恰好交付给一个消 费者(工作进程)。在这一部分中&#xff0c;我们将做一些完全不同的…

孪生神经网络

孪生神经网络 孪生神经网络&#xff08;Siamese network&#xff09;主要用途是比较两图片的相似程度&#xff0c;其核心思想就是权值共享。 卷积神将网络是通过卷积运算提取图像的特征进行训练的&#xff0c;如果想比较两个图像的相似程度&#xff0c;也要对两个图像分别进行…

毛里智慧小学宿舍楼工程量清单编制

目 录 摘 要 I 第1章 前言 1 第2章 招标控制价编制 3 2.1招标控制价 3 2.2建设项目招标控制价汇总表 4 2.3单项工程招标控制价汇总表 5 2.4单项工程招标控制价汇总表 14 2.5分部分项工程和单价措施项目清单与计价表 24 2.6总价措施项目清单与计价表 27 2.7综合单价分析表 28 2.…

C语言学习之路(基础篇)—— 文件操作(上)

说明&#xff1a;该篇博客是博主一字一码编写的&#xff0c;实属不易&#xff0c;请尊重原创&#xff0c;谢谢大家&#xff01; 概述 1) 磁盘文件和设备文件 磁盘文件 指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上&#xff0c;使用时才调入内存。 设备文件 在操作…

亚马逊云科技re:Invent:Serverless是所有构想的核心

12月2日&#xff0c;2022亚马逊云科技re:Invent全球大会上&#xff0c;Amazon.com副总裁兼首席技术官Werner Vogels博士向开发者们展示了另一种可能。在一系列Serverless工具的帮助下&#xff0c;一些代码可以少写&#xff0c;因为未来你可能再也不需要写它们了。这恐怕是自云原…

包装类-Wrapper

包装类的分类 针对八种基本数据类型相应的引用类型-包装类有了类的特点&#xff0c;就可以调用对应的类中的方法 装箱和拆箱 Java是一种面向对象的编程语言&#xff0c;学习Java时就被明确灌输了一个概念&#xff1a;OOP&#xff0c;即面向对象编程。一切皆对象。但是基本…

[附源码]JAVA毕业设计框架的电脑测评系统(系统+LW)

[附源码]JAVA毕业设计框架的电脑测评系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技…

Win11右键菜单反应慢有延迟解决方法分享

Win11右键菜单反应慢有延迟解决方法分享。有用户发现电脑鼠标点击右键菜单的时候&#xff0c;会出现一些延迟&#xff0c;导致自己在使用的过程中非常难受。那么这个问题如何自己去进行解决呢&#xff1f;我们一起来看看详细的解决方法分享吧。 解决方法&#xff1a; 注意&…

物联网IoT体系结构及核心技术

物联网&#xff0c;英文名为Internet of things&#xff08;IoT&#xff09;&#xff0c;顾名思义&#xff0c;物联网就是物物相连的互联网。 这有两层意思&#xff1a; 1、物联网的核心和基础仍然是互联网&#xff0c;是在互联网基础上的延伸和扩展的网络&#xff1b; 2、从…

超级详细 的 Redis 安装教程

超级详细 的 Redis 安装教程 Windows 版本的 Redis 是 Microsoft 的开源部门提供的 Redis. 这个版本的 Redis 适合开发人员学习使用&#xff0c;生产环境中使用 Linux 系统上的 Redis, 这里讲解了这两种的安装和下载。按照你们需要的liunx 或window步骤来 就可以了&#xff08;…

智能优化算法:法医调查优化算法 - 附代码

智能优化算法&#xff1a;法医调查优化算法 摘要&#xff1a;法医调查优化算法( Forensic-based investigation algorithm, FBI), 是由 Jui-Sheng Chou 等于2020 年提出的一种群体智能优化算法。其灵感来源于警官调查嫌疑人的过程。 1.法医调查优化算法 警察的大规模案件调查…

Java并发编程—线程详解

文章目录线程简介什么是线程多线程的使用什么时候需要使用多线程&#xff1f;写多少个线程比较合适&#xff1f;线程优先级靠谱的让出CPU的方法&#xff1f;线程的状态线程的状态有哪几种&#xff1f;线程的状态转换Daemon线程启动和终止线程构造线程启动线程理解中断如何安全的…

[附源码]计算机毕业设计基于Springboot的物品交换平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

逻辑回归模型和Python代码实现

文章目录逻辑回归原理sigmoid函数优化建模代码实现自编代码sklearn代码代码测试原理测试交叉验证逻辑回归原理 此前介绍的线性回归基本模型和增加了正则项的优化模型都只能用来预测连续值&#xff08;标签值是多少&#xff09;&#xff0c;如果想要应用于分类问题&#xff08;…