• 深入理解Java ArrayList集合及其源码详解


    一、ArrayList简单概述

    出身

    • ArrayList是Java集合框架(Java Collections Framework)中List接口的一个实现。
    • 它底层是一个动态数组,用于存储一组有序的元素。
      • 注意:这个有序不是排序后的“有序”;而是创建时有序的存储,即取出的时候跟插入时的顺序一致,比如:排队的时候一个接一个的排,而不是乱排,这样的队伍就是有序的。

    优点:

    • ArrayList可以通过下标访问,提供了更高效的随机访问性能,同时还可以根据需要自动调整其大小。

    缺点:

    • ArrayList的删除元素操作可能会导致性能问题,因为删除元素时需要将后面的元素向前移一位。
    • 同样ArrayList的插入元素操作可能会导致性能问题,因为插入元素时需要将后面的元素向后移一位。

    ArrayList的原理

    ArrayList基于数组实现,这意味着它可以使用索引快速地访问和修改元素。当数组中的元素达到其容量上限时,ArrayList会创建一个新的、更大的数组,并将现有元素复制到新数组中。这个过程称为扩容(Resizing)。扩容过程是自动进行的,开发者无需手动处理。

    ArrayList的特性

    1. 动态扩容:ArrayList可以根据需要自动调整其大小。当元素数量超过当前容量时,它会创建一个新的、更大的数组,并将现有元素复制到新数组中。

    2. 随机访问:由于ArrayList是基于数组实现的,因此它提供了快速的随机访问性能。通过索引,可以直接访问和修改元素。

    3. 重复元素:ArrayList可以包含重复元素。

    二、ArrayList的源码详解

    属性

     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;
    
        /**
         * ArrayList的大小(包含的元素数)。
         */
        private int size;
    

    构造方法

    
     public ArrayList(int initialCapacity) {// 构造一个具有指定初始容量的空列表。
            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() {//构造一个初始容量为 10 的空列表。
            this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//默认值是10
        }
    
       //构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
        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;
            }
        }
    
    

    常用方法

    • 插入元素 boolean add(E e)
        public boolean add(E e) {
            ensureCapacityInternal(size + 1);  //容量加一
            elementData[size++] = e;// 从末尾加入
            return true;
        }
    
    • 指定位置插入 void add(int index, E element)
      public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  //容量加一
            // 调用System.arraycopy创建新数组,再将旧数组拷贝进去
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
    
    • 根据下标取元素 public E get(int index)
        public E get(int index) {
        // 调用下标范围检查方法,检查下标是否越界,若越界抛出IndexOutOfBoundsException
            rangeCheck(index);
    
            return elementData(index);//调用查找元素方法,返回元素
        }
    
    • 根据下标用指定元素替换指定下标元素,并返回旧元素
    • public E set(int index, E element)
       public E set(int index, E element) {
       // 调用下标范围检查方法,检查下标是否越界,若越界抛出IndexOutOfBoundsException
            rangeCheck(index);
    		//寻找被替换元素
            E oldValue = elementData(index);// 调用查找元素方法,返回元素
            elementData[index] = element;// 用新元素覆盖旧元素
            return oldValue;// 返回旧元素
        }
    
    • 移除此列表中指定位置上的元素public E remove(int index)
        public E remove(int index) {
         // 调用下标范围检查方法,检查下标是否越界,若越界抛出IndexOutOfBoundsException
            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;//末尾设为null
    
            return oldValue;//返回被移除元素
        }
    
    • 移除此列表中的所有元素 void clear()
      public void clear() {
            modCount++;
    
            // 遍历列表,并将所以元素设为null
            for (int i = 0; i < size; i++)
                elementData[i] = null;
    
            size = 0;//容量设为0
        }
    

    三、ArrayList常用方法的演示

    1. 创建ArrayList:要创建一个ArrayList,可以使用以下构造方法:

      • 无参构造方法:ArrayList()
      • 带有一个参数的构造方法:ArrayList(Collection c)
      • 带有一个初始容量参数的构造方法:ArrayList(int initialCapacity)
    2. 添加元素:可以使用add(E e)方法向ArrayList中添加元素。如果需要添加多个元素,可以使用addAll(Collection c)方法。

    3. 删除元素:可以使用remove(Object o)方法删除指定元素的第一个匹配项。如果要删除所有匹配的元素,可以使用removeAll(Collection c)方法。

    4. 修改元素:可以使用set(int index, E element)方法修改指定索引处的元素。

    5. 访问元素:可以使用get(int index)方法通过索引访问元素。

    6. 遍历元素:可以使用迭代器(Iterator)增强型for循环for循环遍历ArrayList中的元素。

    示例代码

    下面是一个简单的示例,展示了如何使用ArrayList:

    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class ArrayListExample {
        public static void main(String[] args) {
            // 创建一个ArrayList
            ArrayList<String> list = new ArrayList<>();
    
            // 添加元素
            list.add("Apple");
            list.add("Banana");
            list.add("Cherry");
    
            // 使用迭代器遍历元素
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                String element = iterator.next();
                System.out.println(element);
            }
    
            // 使用增强型for循环遍历元素
            for (String element : list) {
                System.out.println(element);
            }
    
    		// 使用增强型for循环遍历元素
    		for (int i = 0; i < list.size(); i++) {
                System.out.print(list.get(i)+" ");
            }
    
            // 修改元素
            list.set(1, "Orange");
    
            // 删除元素
            list.remove("Banana");
    
            // 打印ArrayList
            System.out.println(list);
        }
    }
    

    迭代器(Iterator)或增强型for循环和for循环遍历效率对比

    在这里插入图片描述

    • 通过上图发现,for循环效率最高,增强for循环次之,迭代器最低
    • 这只是元素比较少,元素多的时候会更加明显。

    最后

    如果感觉有收获的话,点个赞 👍🏻 吧。
    ❤️❤️❤️本人菜鸟修行期,如有错误,欢迎各位大佬评论批评指正!😄😄😄

    💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍在这里插入图片描述

  • 相关阅读:
    22-07-26 西安 ElasticSearch(01)
    C++学习笔记(三)
    MyBatis面试题(二)
    网络安全(黑客)自学
    Mac brew -v 报错 fatal: detected dubious ownership in repository
    【C++】近期知识点杂记(getline、cin、nullptr与C语言风格的字符串、stoi函数、new)
    技术大佬们都是怎么学习的?
    ElasticSearch(三)【索引、映射、文档】
    应用没“积分”,系统就不让运行?Android 13部分功能曝光
    【Proteus仿真】【STM32单片机】便携式血糖仪
  • 原文地址:https://blog.csdn.net/m0_73940426/article/details/139441317