• 常用数据结构之ArrayList


    前言

        ArrayList想必是广大Java程序员开发时最常用的数据结构了,但不一定对其原理都有了解,今天我将结合ArrayList的源码对其进行讲解。本文将围绕ArrayList主要特性(包括适用场景、初始大小、扩容等)、数据存放方式、核心方法实现、其他特性等四个方面进行讲解。

    一、ArrayList特性

        ArrayList是基于数组的数据结构,与LinkedList相比,更加适合在查询多、增删操作少的场景下使用,并且它是非线程安全的,如果并发量比较大的场景,需要改用线程安全的版本或者用JUC包中的CopyOnWriteArrayList。

        它的初始数组大小为10,由下图所示的成员变量控制:

         当新加元素时原数组已经满了,则会触发扩容,扩容策略为将原数组长度*1.5,代码中是用右移位运算实现的,源码如下所示:

    二、数据存放方式

        ArrayList是以数组的方式存放数据的,Object[],如下所示:

    三、核心方法实现

        我们看最常用的add方法:

    1 public boolean add(E e) {
    2         ensureCapacityInternal(size + 1);  // 如果已经满了,则扩容
    3         elementData[size++] = e;
    4         return true;
    5     }

        下面方法用于判断是否原数组已经满了,如果满了则扩容,不满且未初始化则初始化长度为10,否则不用变化

    1 private void ensureCapacityInternal(int minCapacity) {
    2         ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    3     }

        下面方法用于返回数组需要的最小长度。

    1 private static int calculateCapacity(Object[] elementData, int minCapacity) {
    2         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    3             return Math.max(DEFAULT_CAPACITY, minCapacity);
    4         }
    5         return minCapacity;
    6     }

        下面的方法用于判断是否需要扩容,如果需要则通过grow方法扩容。

    1 private void ensureExplicitCapacity(int minCapacity) {
    2         modCount++;
    3 
    4         // overflow-conscious code
    5         if (minCapacity - elementData.length > 0) // 如果需要的最小长度大于当前数组总长度,则走grow扩容
    6             grow(minCapacity);
    7     }

        下面的grow方法是控制扩容的核心方法:

     1 private void grow(int minCapacity) {
     2         // overflow-conscious code
     3         int oldCapacity = elementData.length;
     4         int newCapacity = oldCapacity + (oldCapacity >> 1);
     5         if (newCapacity - minCapacity < 0) // 如果扩容之后的长度小于需要的最小长度,则取最小长度为待扩容长度,这种情况一般不会出现
     6             newCapacity = minCapacity;
     7         if (newCapacity - MAX_ARRAY_SIZE > 0)
     8             newCapacity = hugeCapacity(minCapacity);
     9         // minCapacity is usually close to size, so this is a win:
    10         elementData = Arrays.copyOf(elementData, newCapacity); // 创建新数组,将老数组的数据复制过去
    11     }

        注意复制数组时,用的Arrays.copyOf方法,该方法最终引用的是System.arraycopy这个native方法实现的数组复制。其他方法更加简单,此处就不一一粘贴源码解读了。

    四、其他特性

    1、关于modCount

        在看ArrayList源码的时候,会发现有一个变量是modCount,在增删改的方法中均涉及到对它的++操作。modCount属性是在AbstractList中定义出来的:

        可以把它理解成每个ArrayList的一个改动的版本号,只要ArrayList有改动,这个版本号就会+1。在通过迭代器对ArrayList里面的元素进行遍历操作时,每次都会比较一下这个版本号是否有变化,如果检测到预期之外的变化,就会抛出常见的那个异常-ConcurrentModificationException:

        ArrayList东西不多,实现也相对比较简单,面试时现在一般会跟Vector或者LinkedList进行比对或者作为多线程的一个引子来问,本文就先到这里,下一期对LinkedList进行解读。

  • 相关阅读:
    二维码生成器
    uniapp 上下左右手势滑动时的事件
    uniapp 类似钉钉的消息时间段显示
    css 币种与价格的底部对齐
    css 设置父元素透明度不影响子元素透明度
    js match方法截取某个字符串前面的内容
    下载电脑系统,服务等...
    uniapp 实现点击、滑动切换tab导航内容
    vue 实现分类渲染数据
    jquery 实现在事件完成后才能再次点击执行
  • 原文地址:https://www.cnblogs.com/zzq6032010/p/11980292.html
Copyright © 2020-2023  润新知