ArrayList源码解读

1.背景

源码解读是提升编程能力的有效方式

在面试中也经常问到…..

2.自己开发一个类似ArrayList的对象

解读源码的最佳方式就是自己开发一个类似的….

ArrayList源码解读

import java.util.AbstractList;
import java.util.Arrays;
import java.util.List;
import java.util.RandomAccess;

/**
*
@author 姿势帝-博客园
* @address
https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 04/04 6:39
* @description

* 需求:自己设计一个基于数组的集合,功能类似ArrayList
* 设计思路
* 1.使用数组来存放添加的元素
* 2.默认数组长度为10,满了后按照原来的1.5倍扩容
* 3.设置的最大数组长度为MAX_ARRAY_SIZE = Integer.MAX_VALUE – 8,实际最大长度不超过 Integer.MAX_VALUE
* 4.编写一个构造方法,一个添加元素的方法,一个获取元素的方法
* 难点:
* 扩容算法,即计算的实际的数组长度
* 总结:
* 当你看懂了我自己写的MyArrayList这个集合类,你再去看ArrayList,
* 你会发现是不是ArrayList是不是"抄袭"我的MyArrayList,居然和我写的一模一样…哈哈哈…

  • /
    public class MyArrayList extends AbstractList
    implements List, RandomAccess, Cloneable, java.io.Serializable {
    /*

    • 默认的空集合
      /
      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
      /*
    • 用于存放数据的集合
    • transient:临时的
    • 本文要介绍的是Java中的transient关键字,transient是短暂的意思。
    • 对于transient 修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略。
    • 因此,transient变量不会贯穿对象的序列化和反序列化,生命周期仅存于调用者的内存中而不会写到磁盘里进行持久化。
      /
      transient Object[] elementData;
      // 数组的默认长度
      private static final int DEFAULT_CAPACITY = 10;
      /最大数组长度
      有些虚拟机会预留出一定长度作为数组的一些头信息,所以数组最大长度达不到Integer的最大值2~32-1 = 2147483647(21亿..)左右,
      这个-8是为了避免内存溢出。
      */
      private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE – 8;
      // 数组长度
      private int size;

    /*
    * 构造方法
    /
    public MyArrayList() {
    // 设一个默认的空数组,Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /*
    * 添加一个元素
    *
    *
    @param e
    *
    @return
    /
    @Override
    public boolean add(E e) {
    // 确保数组长度足够,需要的最小长度为当前数组的长度+1
    ensureCapacityInternal(size + 1);
    elementData[size
    ++] = e;
    return true;
    }

    /*
    * 确保数组的容量不出界
    *
    *
    @param minCapacity
    /
    private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    /*
    * 计算数组的长度(容量)
    *
    *
    @param elementData 集合的数组
    *
    @param minCapacity 需要的长度
    *
    @return 返回一个数组长度
    /
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    // 如果数组为默认的空数组
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    // 在默认的数组长度10 与 需要的长度中取最大值
    return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
    }

    /*
    * modCount 解读
    * modCount只记录arraylist被修改次数,在每次添加修改删除的时候进行+1,
    * 这个参数的作用是在多线程并发的时候,由于arraylist不是线程安全的,在当前线程进行遍历或其他操作的时候,
    * 如果其它线程修改了改值,那么当前线程能检测到该参数发生变化,会抛异常。
    *
    *
    @param minCapacity
    /
    private void ensureExplicitCapacity(int minCapacity) {
    // 累加集合的修改次数
    modCount++;
    // 如果需要的数组长度大于了实际的数组长度,则进行扩容
    if (minCapacity – elementData.length > 0)
    grow(minCapacity);
    }

    /
    * 扩容算法
    *
    *
    @param minCapacity
    /
    private void grow(int minCapacity) {
    // 原来的数组长度
    int oldCapacity = elementData.length;
    / 新的数组长度,
    * 1.oldCapacity >> 1 右移动2位表示除2,即新长度为原来的+原来的一半(若原来为10,变为15)
    * 2.oldCapacity 第一次进入的时候 oldCapacity =0,因此计算出来的newCapacity=0
    */
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 加一半后长度还是不够,则用传递过来的数值(addAll来说,就是原长度+新集合长度)
    if (newCapacity – minCapacity < 0)
    newCapacity
    = minCapacity;
    // 新的数组长度大于了设置的最大长度时,计算一个新的长度出来
    if (newCapacity – MAX_ARRAY_SIZE > 0)
    newCapacity
    = hugeCapacity(minCapacity);
    // 执行扩容,扩容的实现时把原来的数组复制到新的数组中来,新的数组长度就是newCapacity
    elementData = Arrays.copyOf(elementData, newCapacity);
    }

    /*
    * 数组最大值计算
    *
    *
    @param minCapacity 数组的当前需要的最小长度
    /
    private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0)
    throw new OutOfMemoryError();
    // 三目运算 , minCapacity大于当前设置的数组最大值时,就取Integer.MAX_VALUE,否则就是设置的最大值
    // MAX_ARRAY_SIZE = Integer.MAX_VALUE – 8;
    return (minCapacity > MAX_ARRAY_SIZE) ?
    Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
    }

    @Override
    public E get(int index) {
    // 检查下标是否越界
    rangeCheck(index);
    return elementData(index);
    }

    private void rangeCheck(int index) {
    if (index >= size)
    throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    private String outOfBoundsMsg(int index) {
    return "Index: " + index + ", Size: " + size();
    }

    @SuppressWarnings("unchecked")
    E elementData(
    int index) {
    // 从数组中取出一个元素
    return (E) elementData[index];
    }

    @Override
    public int size() {
    return size;
    }
    }
    View Code

3.源码解读

3.1.构造方法解读

public ArrayList() {    // 设一个默认的空数组,Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};    // 开始new ArrayList()的时候,数组长度其实是0,并不是10,是个空数组。    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

3.2.add方法解读

@Overridepublic boolean add(E e) {    // 确保数组长度足够,需要的最小长度为当前数组的长度+1    ensureCapacityInternal(size + 1);    // 放在当前元素的下一个位置    elementData[size++] = e;    return true;}
确定数组长度的方法ensureCapacityInternal
private void ensureCapacityInternal(int minCapacity) {    // calculateCapacity 计算需要数组长度minCapacity    // ensureExplicitCapacity 确定明确的值,即是否扩容    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}

calculateCapacity 计算需要数组长度minCapacity

private static int calculateCapacity(Object[] elementData, int minCapacity) {    // 如果数组为默认的空数组    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {        // 在默认的数组长度10 与 需要的长度中取最大值        return Math.max(DEFAULT_CAPACITY, minCapacity);    }    return minCapacity;}
ensureExplicitCapacity 确定明确的值,即是否扩容

undefined

/** * modCount 解读 * modCount只记录arraylist被修改次数,在每次添加修改删除的时候进行+1, * 这个参数的作用是在多线程并发的时候,由于arraylist不是线程安全的,在当前线程进行遍历或其他操作的时候, * 如果其它线程修改了改值,那么当前线程能检测到该参数发生变化,会抛异常。 * * @param minCapacity */private void ensureExplicitCapacity(int minCapacity) {    // 累加集合的修改次数    modCount++;    // 如果需要的数组长度大于了实际的数组长度,则进行扩容    if (minCapacity - elementData.length > 0)        grow(minCapacity);}

grow扩容算法

/
* 扩容算法

* @param minCapacity
/
private void grow(int minCapacity) {
// 原来的数组长度
int oldCapacity = elementData.length;
/ 新的数组长度,
* 1.oldCapacity >> 1 右移动2位表示除2,即新长度为原来的+原来的一半(若原来为10,变为15)
* 2.oldCapacity 第一次进入的时候 oldCapacity =0,因此计算出来的newCapacity=0
/
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 加一半后长度还是不够,则用传递过来的数值(addAll来说,就是原长度+新集合长度)
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 新的数组长度大于了设置的最大长度时,计算一个新的长度出来
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 执行扩容,扩容的实现时把原来的数组复制到新的数组中来,新的数组长度就是newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}

最大长度计算

/
* 数组最大值计算

* @param minCapacity 数组的当前需要的最小长度
/
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0)
throw new OutOfMemoryError();
// 三目运算 , minCapacity大于当前设置的数组最大值时,就取Integer.MAX_VALUE,否则就是设置的最大值
// MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

3.3get方法解读

这个比较简单

@Overridepublic E get(int index) {    // 检查下标是否越界    rangeCheck(index);    // 获取一个元素    return elementData(index);}/** * 检查下标是否越界 * @param index */private void rangeCheck(int index) {    if (index >= size)        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));}private String outOfBoundsMsg(int index) {    return "Index: " + index + ", Size: " + size();}@SuppressWarnings("unchecked")E elementData(int index) {    // 从数组中取出一个元素    return (E) elementData[index];}

3.4.size方法解读

@Overridepublic int size() {    return size;}

4.总结

新增一个元素,调用add方法,
如果长度没超出,那么就在数组下个位置放置该元素,
如果超出了,按照当前长度的一半增长,采用的是右位移的算法(如果对位移等基础知识不清楚的可以看之前讲的<

最后:其实你采用断点的方式debug一下,或许对源码的理解会更加深入.

完美!

Original: https://www.cnblogs.com/newAndHui/p/16101626.html
Author: 李东平|一线码农
Title: ArrayList源码解读

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/604245/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Latex符号表

    描述 语法 示例 下划线 \underline{Hello} 不等于 \neq 低省略号 \dots 高省略号 \cdots 右属于 \in 左属于 \ni 不属于 \notin …

    数据结构和算法 2023年6月12日
    080
  • A与B的对话之线索二叉树

    A:什么是线索二叉树? B:先别着急,你知道二叉树的定义的存储结构是什么吗? A:当然知道,就是包含一个值域和两个指针域的结构体,下图所示 typedef struct node …

    数据结构和算法 2023年6月7日
    092
  • 从面试考察英语,想到的线性思维和立体思维

    请问,技术面试的时候需要考察英语吗? 大部分人如此回答: “代码写的好就行,考察英语干什么?” “又不是外企,都是中国人,要什么英语&#8221…

    数据结构和算法 2023年6月7日
    0101
  • plicp 点云迭代最近邻点配准法

    输入参数 点云A的极坐标集合 点云A对应Lidar所在pose 点云B的极坐标集合 点云B对应Lidar所在pose Features 根据两个点云的弧度关系确定找点的起始位置 根…

    数据结构和算法 2023年6月16日
    085
  • 树剖笔记

    之前学的,但是这种恶心的东西一天不打就忘了。 为了之后会议方便,就简单的写一下吧。 树剖,顾名思义就是把树剖成一条一条的链。但是要按照儿子个数分成 轻链和 重链。 每一条链上节点的…

    数据结构和算法 2023年6月7日
    085
  • Java中比较两个对象

    在Java中比较两个对象我们知道不能使用 ==来进行比较,例如在比较两个字符串时要使用 equals方法来比较。但这里需要注意的是String、Integer等一些包装类已经替我们…

    数据结构和算法 2023年6月8日
    074
  • Lua CallbackHell优化

    概述 在异步操作中,常常要使用回调。但是,回调的嵌套常常会导致逻辑混乱,一步错步步错,难以维护。在Lua中,可以使用协程进行优化。 问题分析 模拟一个回合制游戏攻击过程 local…

    数据结构和算法 2023年6月8日
    082
  • 卡特兰数学习笔记

    卡特兰数(Catalan 数)学习笔记 一、引入 问题 1 由 (n) 个 (+1) 和 (n) 个 (-1) 组成的 (2n) 项序列 (a_1,a_2,\cdots,a_{2n…

    数据结构和算法 2023年6月8日
    088
  • 拷贝构造函数调用时机

    C++中拷贝构造函数调用时机通常有三种情况1.使用一个已经创建完毕的对象来初始化一个对象 Person p1(20); Person p2(p1); 2.值传递的方式给函数参数传值…

    数据结构和算法 2023年6月7日
    081
  • 并查集(UnionFind)

    并查集和其他树形结构不一样,是由孩子指向父亲,它解决了一些连接问题,怎么才能确定两个点是否相连呢?并查集可以非常快的确定两个点是否连接。 如何确定连个点是否连接呢? 我们可以用一个…

    数据结构和算法 2023年6月8日
    0105
  • 树链剖分

    推荐博客: https://www.cnblogs.com/ivanovcraft/p/9019090.html 这讲的很好,主要是用来处理树上路径问题的高效算法 首先明确概念: …

    数据结构和算法 2023年6月7日
    0101
  • 【设计模式】之责任链模式

    定义 责任链模式(Chain of Responsibility Pattern)中,有一条由请求处理者对象组成的链条,每个对象(除最后一个对象外)都持有下一个对象的引用,请求发送…

    数据结构和算法 2023年6月12日
    085
  • [总结]2022/1/22

    P1心路历程 开题看到T1,直接放弃。(因为恶心的题目要求导致暴力很难写)然后看T2,一开始看到觉得很疑惑:输入数据呢???然后反复读了几遍题,才发现输入数据(或者说是需要的数据)…

    数据结构和算法 2023年6月8日
    0130
  • 线段树(SegmentTree)

    对于数组应用于区间染色实现为On,而线段树是O(logn) 什么是线段树:对于一个二叉树,每一个节点存储的是一个线段或是一个区间相应的信息。 查询 更新 #pragma once …

    数据结构和算法 2023年6月8日
    0110
  • 链表(单链表)的多种功能实现

    单链表 简介 单链表的多种功能实现 代码 点击查看代码 #include<stdio.h> #include<stdlib.h> #include<m…

    数据结构和算法 2023年6月12日
    077
  • springboot自动配置原理以及手动实现配置类

    springboot自动配置原理以及手动实现配置类 1、原理 spring有一个思想是”约定大于配置”。 配置类自动配置可以帮助开发人员更加专注于业务逻辑开…

    数据结构和算法 2023年6月16日
    084
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球