Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >ArrayList 源码分析

ArrayList 源码分析

作者头像
希希里之海
发布于 2019-09-05 08:55:07
发布于 2019-09-05 08:55:07
47800
代码可运行
举报
文章被收录于专栏:weixuqin 的专栏weixuqin 的专栏
运行总次数:0
代码可运行

目录

ArrayList 源码分析

1. 数组介绍

数组是数据结构中很基本的结构,很多编程语言都内置数组。

Java 中当创建数组时会在内存中划分一块连续的内存,然后当有数据进入的时候会将数据按顺序的存储在这块连续的内存中。当需要读取数组中的数据时,需要提供数组中的索引,然后数组根据索引将内存中的数据取出来,返回给读取程序。在 Java 中并不是所有的数据都能存储到数组中,只有相同类型的数据才可以一起存储到数组中。

注意: 因为数组在存储数据时是按顺序存储的,存储数据的内存也是连续的,所以它的特点就是寻址读取数据比较容易(直接通过下标读取),插入和删除比较困难(需要移动元素)

2. ArrayList 源码分析

alt + 7:查看一个类中的所有方法

构造方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 指定长度
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 创建指定长度的 Object 数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

// 空参构造方法
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 传递集合对象方法
public ArrayList(Collection<? extends E> c) {
    // 转换为数组
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        // 这是一个 bug,c.toArray 有可能返回的不是 Object[] 类型,此问题在 JDK9 已经被修复
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 数组长度为0,返回空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
插入数据
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 插入元素
public boolean add(E e) {
    // 检测是否扩容
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

// 指定位置插入元素
public void add(int index, E element) {
    // 判断 index 是否越界
    rangeCheckForAdd(index);
    // 检测是否需要扩容
    ensureCapacityInternal(size + 1); 
    // 将 index 之后的所有元素向后移动一位
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    // 将 index 位置覆盖新值
    elementData[index] = element;
    // 长度加 1
    size++;
}
扩容操作
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 扩容的入口方法
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 计算最小容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 默认容量 10
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

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

    // 超过过数组长度则进行扩容,若没有初始化容量大小,则默认为10
    // 是否满足扩容的条件 最小容量 - object 数组的长度
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
// 数组扩容方法
private void grow(int minCapacity) {
    // 当前数组长度
    int oldCapacity = elementData.length;
    // 新的数组容量 = 老容量 + 老容量 / 2(1.5倍)【右移一位即 / 2】
    // eg: oldCapacity = 10
    // oldCapacity >> 1
    // 0000 1010 >> 1
    // 0000 0101 = 5
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    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);
}
删除方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 指定下标删除
public E remove(int index) {
    // 检测 index 值是否越界,IndexOutOfBoundsException
    rangeCheck(index);

    modCount++;
    // 返回要删除的元素
    E oldValue = elementData(index);
    // 将 index+1 及之后的元素向前移动一位,覆盖被删除的值
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 将最后一个位置的元素清空
    elementData[--size] = null; 

    return oldValue;
}

// 指定元素删除
public boolean remove(Object o) {
    // 判断元素是否为 null
    if (o == 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;
            }
    }
    // 如果没有匹配,返回 false
    return false;
}

// 快速删除,没有检测 index 下标
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; 
}
遍历
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ArrayList<Integer> list = new ArrayList<>();
        
list.add(10);
list.add(11);
list.add(12);
list.add(13);
list.add(15);

// for-each 删除
for (Integer num : list) {
    if (num == 12) {
        list.remove(num);
    }
}

// 报错
// Exception in thread "main" java.util.ConcurrentModificationException

// 因为 ArrayList 源码中
final void checkForComodification() {
    // modCount 值和 expectedModCount 值不相等就异常
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

// 解决异常问题
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    Integer num = it.next();
    if (num == 12) {
        // 使用迭代器的删除方法
        it.remove();
    }
}
  • for-each 方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void forEachRemaining(Consumer<? super E> consumer) {
    Objects.requireNonNull(consumer);
    final int size = ArrayList.this.size;
    int i = cursor;
    if (i >= size) {
    return;
    }
    final Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length) {
    throw new ConcurrentModificationException();
    }
    while (i != size && modCount == expectedModCount) {
    consumer.accept((E) elementData[i++]);
    }
    // update once at end of iteration to reduce heap write traffic
    cursor = i;
    lastRet = i - 1;
    checkForComodification();
}
  • iterator 类中的 remove()方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        // 修改 expectedModCount 值和当前 modCount 一样
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

可以看到 Iterator 类中的 remove() 方法中将 expectedModCount 值修改成和当前 modCount 一样,便不会抛出 ConcurrentModificationException 异常。

总结:我们应该使用 迭代器 Iterator 来删除元素而不应该使用 for-each 方法。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-09-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
ArrayList 源码解析
size: 当前数组的大小(实时),类型 int,没有使用 volatile 修饰,非线程安全的
飞翔的竹蜻蜓
2020/07/08
3540
ArrayList 源码解析
jdk1.8源码阅读ArrayList
   ArrayList的实现原理就是大学数据结构书本中的动态数组原理,初始化一个Object数组,然后对Object数组进行插入,扩容,查找,删除等操作。所以可以看出java引用类型所占内存大小是一样的,Object数组类似于c语言中的void*指针数组,每个指针在64位机器上都占8字节, 在hotspot jvm中java引用类型也是占8字节。所以ArrayList无法存放基本类型,只能存放引用类型。以下分析ArrayList最基础,最常用的操作。
用户4415180
2022/06/23
2070
jdk1.8源码阅读ArrayList
源码阅读之ArrayList
源码阅读是基于JDK7,本篇主要涉及ArrayList常用方法源码分析。 1.概述 ArrayList是List接口的可调整大小的数组实现,可以包含任何类型的元素,包括null。除了实现List接口中的方法,它还提供了调整元素数组大小的方法。这个类除了非同步的特性,大体上和Vector是相同的。它的size、isEmpty、get、set方法运行时间为常数,而add方法运行开销为分摊的常数,添加n个元素的时间复杂度是O(n)。每个ArrayList的实例都有自己的容量,这个容量即用于存储List元素的数组大
JavaQ
2018/04/04
6840
源码阅读之ArrayList
ArrayList源码分析
可以看到,在构造方法中直接将 elementData 指向 DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组,这个时候该ArrayList的size为初始值0。
周同学
2019/08/29
4980
ArrayList源码分析
ArrayList源码完全分析
本文介绍了Java中的ArrayList类,它是Java编程语言中最常用的集合类之一。ArrayList用于存储一系列元素,允许添加、删除和随机访问元素。ArrayList的底层实现是数组,它通过ArrayList的实例变量来存储元素。在ArrayList中,添加、删除和随机访问元素的时间复杂度分别是O(1)、O(1)和O(1)。ArrayList的常用方法有add、remove、set、get、size、iterator、listIterator等。同时,文章还对ArrayList的性能、容量预估、扩容、元素访问顺序等方面进行了详细阐述。
MelonTeam
2018/01/04
9820
ArrayList 源码分析
ArrayList 源码分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方便,所以在 github 上提供JDK1.8 的源码、详细的注释及测试用例。欢迎大家 star、fork ! 2. 由于个人水平有限,对源码的分析理解可能存在偏差或不透彻的地方还请大家在评论区指出,谢谢! 1. 结构   首先我们需要对 ArrayList 有一个大致的了解就从结构来看看吧. 1. 继承   该类继承自 AbstractList 这个比较好
lwen
2018/04/17
6360
ArrayList源码解析
最近在整理数据结构和算法,发现java的源码很优秀。就学习一下, 这里做个记录。 我使用的是jdk
若与
2019/11/03
3460
ArrayList 源码分析
ArrayList 是基于数组实现的,继承 AbstractList, 实现了 List、RandomAccess、Cloneable、Serializable 接口,支持随机访问。
小旋锋
2019/01/21
3900
ArrayList源码详解
ArrayList UML类图 ArrayList 概述 ArrayList 是实现 List 接口的动态数组,所谓动态就是它的大小是可变的。实现了所有可选列表操作,并允许包括 null 在内的所有
秋白
2018/05/24
5660
【Java入门提高篇】Day21 容器类详解(四)ArrayList源码分析
   今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作。
弗兰克的猫
2018/06/03
7480
【Java入门提高篇】Day21 容器类详解(四)ArrayList源码分析
Java ArrayList源码分析,带你拿下面试官(含扩容机制等重点问题分析)
这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解。所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项目,专注 Java 后端面试题 + 解析 + 重点知识详解 + 精选文章的开源项目,希望它能伴随你我一直进步!
BWH_Steven
2021/02/24
1.7K1
Java ArrayList源码分析,带你拿下面试官(含扩容机制等重点问题分析)
教你如何高效使用Java中的ArrayList
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
喵手
2023/11/17
4990
教你如何高效使用Java中的ArrayList
深入探讨源码--ArrayList
深入探讨源码之ArrayList ArrayList 类图ArrayList的数据结构ArrayList的关键属性ArrayList构造方法ArrayList常用方法add方法ArrayList中的fast-fail机制add(i,o)方法set(i,o)方法get(i)方法remove(index)方法remove(Object)方法clear方法indexOf(o)方法
田维常
2020/03/25
2360
ArrayList源码分析
源码中的重要属性 DEFAULT_CAPACITY:默认初始数组大小,默认值是10,无参构造器初始化是空数组,在第一次add()操作后扩容成10 elementData:真正存放数据的数组 size:当前数组中真正存放的元素个数 构造器 提供了三种构造器,分别是无参、指定大小、指定初始数据的构造器 // 无参构造器,初始化为一个空数组,在第一次add操作时将数组扩容为默认初始数组大小10 public ArrayList() { this.elementData = DEFAULT
Krains
2020/08/19
2030
ArrayList源码解析,老哥,来一起复习一哈?
JDK源码解析系列文章,都是基于JDK8分析的,虽然JDK14已经出来,但是JDK8我还不会,我…
敖丙
2020/05/26
6580
ArrayList源码解析,老哥,来一起复习一哈?
JDK1.8源码(五)——java.util.ArrayList 类
  关于 JDK 的集合类的整体介绍可以看这张图,本篇博客我们不系统的介绍整个集合的构造,重点是介绍 ArrayList 类是如何实现的。 1、ArrayList 定义 ArrayList 是一个用
IT可乐
2018/03/30
1.1K0
JDK1.8源码(五)——java.util.ArrayList 类
彻底搞懂ArrayList
同样是空的数组对象,和EMPTY_ELEMENTDATA区别在于,无参构造函数的空数组会用DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值,有参构造函数的空数组会用EMPTY_ELEMENTDATA赋值。
每天学Java
2020/09/18
4590
Java面试题之List(一)
我们都知道,Java面试中避免不了的要涉及到对各种框架和源码的盘问。有的面试官会直接点,问我们对ArrayList的源码的理解,有的会间接的提问。
黑洞代码
2021/01/14
4410
ArrayList详解
ArrayList 是一个数组列表。它的主要底层实现是Object数组,但与 Java 中的数组相比,它的容量能动态变化,可看作是一个动态数组结构。特别注意的是,当我们装载的是基本类型的数据 int,long,boolean,short,byte… 的时候,我们只能存储他们对应的包装类。
程序员阿杜
2023/08/25
2850
jdk1.8ArrayList主要方法和扩容机制(源码解析)
ArrayList实现了List接口它是一个可调整大小的数组可以用来存放各种形式的数据。并提供了包括CRUD在内的多种方法可以对数据进行操作但是它不是线程安全的,外ArrayList按照插入的顺序来存放数据。
全栈程序员站长
2022/09/05
3820
jdk1.8ArrayList主要方法和扩容机制(源码解析)
相关推荐
ArrayList 源码解析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验