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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
今天,我要干掉 if ... else ...
近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:
没有故事的陈师傅
2021/04/26
5770
今天,我要干掉 if ... else ...
抛弃繁杂的if判断,使用它试一试!
近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:
Java技术精选
2021/09/15
3000
Java规则引擎drools:drt动态生成规则并附上具体项目逻辑
由于本人的码云太多太乱了,于是决定一个一个的整合到一个springboot项目里面。
ydymz
2018/09/05
5.4K0
一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService
本文进行JavaSE基础内容:Executor执行器体系的整体介绍。该文是整体框架介绍,并非局限于某一个使用的细节。由于我不止一次的被咨询说ExecutorService和ScheduledExecutorService什么区别和联系,以及ThreadPoolExecutor和ThreadPoolTaskExecutor有什么不一样之类的问题,因此决定写此文科普一下。
YourBatman
2020/04/08
3K0
一文搞懂Executor执行器和线程池的关系,整体介绍其任务执行/调度体系:ThreadPoolExecutor、ScheduledExecutorService
徒手撸了一个API网关,理解更透彻了,代码已上传github,自取~
来源 | cnblogs.com/2YSP/p/14223892.html 一、背景 最近在github上看了soul网关的设计,突然就来了兴趣准备自己从零开始写一个高性能的网关。经过两周时间的开发,
猿天地
2021/02/22
7510
徒手撸了一个API网关,理解更透彻了,代码已上传github,自取~
30个类手写Spring核心原理之自定义ORM(上)(6)
说到ResultSet,有Java开发经验的“小伙伴”自然最熟悉不过了,不过我相信对于大多数人来说也算是“最熟悉的陌生人”。从ResultSet取值操作大家都会,比如:
Tom弹架构
2021/12/16
5620
Java各种规则引擎
(2)新建配置文件/src/resources/META-INF/kmodule.xml
matinal
2020/11/27
5.3K1
Java各种规则引擎
Executor执行器与线程池
Java使用Executor框架执行多线程任务,创建与操作系统线程一对一的映射线程,由操作系统分配CPU来执行。称为任务的两级调度模型,如下图所示:
搬砖俱乐部
2019/06/15
9670
编程写判断还在用if?试试规则执行器是不是用的更顺手!
近日在公司领到一个小需求,需要对之前已有的试用用户申请规则进行拓展。我们的场景大概如下所示:
Java程序猿
2021/06/18
4230
dubbo路由代码分析3(condition路由器)
这篇说说,dubbo condition类型路由器的路由解析和执行过程 由 https://cloud.tencent.com/developer/article/1109552 这篇我们可以看到 condition类型路由器是由condition路由工厂获取,源码如下 public class ConditionRouterFactory implements RouterFactory { public static final String NAME = "condition";
技术蓝海
2018/04/26
1.5K0
dubbo路由代码分析3(condition路由器)
XXL-JOB系列二之执行器注册
如果是在基于Spring的项目中使用xxl-job,那么是由XxlJobSpringExecutor这个类来进行JobHanlder的初始化,首先这个类实现了SmartInitializingSingleton接口,这个接口的作用是在Spring容器管理的所有单例对象(非懒加载)完成实例化之后执行其回调方法afterSingletonsInstantiated, 在该方法中由initJobHanlderMethodRepository去完成初始化操作
用户9511949
2024/06/27
5340
报警系统QuickAlarm之报警执行器的设计与实现
根据前面一篇总纲的博文,将整体结构划分为了四大块,本文则主要目标集中在第一块,报警执行器(AlarmExecute)的设计与加载上了 主要的关注点无外乎 定义-》加载-》实现逻辑三块了: AlarmExecute 的接口定义 如何加载用户自定义的AlarmExecute AlarmExecute的内部实现 I. AlarmExecute接口定义 在定义接口之前,先来根据几个问题来加深下这个概念的理解: 1. 基础知识 说一下这个报警执行器到底是干嘛的? 执行具体的报警逻辑(感觉说了依据废话) 因此不同的报警
一灰灰blog
2018/03/29
7130
报警系统QuickAlarm之报警执行器的设计与实现
用建造者模式实现一个防SQL注入的ORM框架
以构建一门课程为例,一个完整的课程由PPT课件、回放视频、课堂笔记、课后作业组成,但是这些内容的设置顺序可以随意调整,我们用建造者模式来代入理解一下。首先创建一个产品类Course。
Tom弹架构
2021/10/28
1K0
Spark sql规则执行器RuleExecutor(源码解析)
Spark sql通过Analyzer中 定义的rule把Parsed Logical Plan解析成 Analyzed Logical Plan;通过Optimizer定义的rule把 Analyzed Logical Plan 优化成 Optimized Logical Plan 。
数据仓库践行者
2020/10/29
1.5K0
Spark sql规则执行器RuleExecutor(源码解析)
数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 执行
本文主要基于 Sharding-JDBC 1.5.0 正式版 1. 概述 2. ExecutorEngine 2.1 ListeningExecutorService 2.2 关闭 2.3 执行 SQL 任务 3. Executor 3.1 StatementExecutor 3.2 PreparedStatementExecutor 3.3 BatchPreparedStatementExecutor 4. ExecutionEvent 4.1 EventBus 4.2 BestEffortsDelive
芋道源码
2018/03/02
1.2K0
数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 执行
Elastic-Job系列一之执行器注册启动
以springboot为例看下elastic-job的执行器启动流程,启动配置类为elasticjob-lite-spring-boot-starter中的ElasticJobLiteAutoConfiguration,如下
用户9511949
2024/07/07
4160
mybatis源码之执行器解析 原
-从上图中可以看出所有执行器都实现了Executor接口,定义了一些通用的操作,Executor的接口定义如下
用户2603479
2019/08/31
7200
Dubbo 路由机制的实现
Dubbo 路由机制是在服务间的调用时,通过将服务提供者按照设定的路由规则来决定调用哪一个具体的服务。
ytao
2020/06/04
1.1K0
Dubbo 路由机制的实现
SpringBoot入门建站全系列(三十四)使用Drools规则引擎做排班系统
Drools 是用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值。Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。
品茗IT
2020/05/28
2.6K0
if-else 判断语句过多该如何处理?
我们平时在写代码的时候,if-else判断语句基本上必不可少,当我们的判断语句只有一两层的时候,类似下面这种,情况还好,基本上能接受;
Java极客技术
2022/12/04
6260
推荐阅读
相关推荐
今天,我要干掉 if ... else ...
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验