资讯专栏INFORMATION COLUMN

Java集合源码分析系列-(一)ArrayList源码剖析

Miyang / 2780人阅读

摘要:需要注意的是,通过构造函数定义初始量是动态数组的实际大小。带容量的构造函数新建一个容量为的数组默认构造函数,默认为空构造一个包含指定元素的第一个构造方法使用提供的来初始化数组的大小。

前言

今天介绍经常使用的一个Java集合类——ArrayList(基于JDK1.8.0_121)。ArrayList在工作和日常面试中经常被使用或者提到。总的来说,工作中使用ArrayList主要是因为动态数组的方便性,面试中出现ArrayList经常是和LinkedList/Vector一起出现,分析这三种集合的异同。

ArrayList类图

图片是直接从IntelliJ中导出来的,其中:蓝色线条意味着继承,绿色线条意味着接口实现。

ArrayList源码剖析 ArrayList定义
public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable

我们首先需要明白并且牢记在内心的是,ArrayList本质上是一个数组,但是与Java中基础的数组所不同的是,它能够动态增长自己的容量
通过ArrayList的定义,可以知道ArrayList继承了AbstractList,同时实现了List,RandomAccess,Cloneable和java.io.Serializable接口。

继承了AbstractList类,实现了List,意味着ArrayList是一个数组队列,提供了诸如增删改查、遍历等功能。
实现了RandomAccess接口,意味着ArrayList提供了随机访问的功能。RandomAccess接口在Java中是用来被List实现,用来提供快速访问功能的。在ArrayList中,即我们可以通过元素的序号快速获取元素对象。
实现了Cloneable接口,意味着ArrayList实现了clone()函数,能被克隆。
实现了java.io.Serializable接口,意味着ArrayList能够通过序列化进行传输。

ArrayList关键属性
private static final int DEFAULT_CAPACITY = 10;
transient Object[] elementData;
private int size;

(1)ArrayList的默认容量为10;
(2)elementData是"Object类型的数组",所有ArrayList元素都保存在elementData中。在ArrayList中,elementData是一个动态数组。需要注意的是,ArrayList通过构造函数ArrayList(int initialCapacity)定义初始量initialCapacity;
(3)size是动态数组的实际大小。

//ArrayList带容量的构造函数
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    //新建一个容量为initialCapacity的数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}
//ArrayList默认构造函数,默认为空
public ArrayList() {

    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 构造一个包含指定元素的list
public ArrayList(Collection c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

第一个构造方法使用提供的initialCapacity来初始化elementData数组的大小。
第二个构造方法默认数组为0。
第三个构造方法则将提供的集合转成数组返回给elementData(返回若不是Object[]将调用Arrays.copyOf方法将其转为Object[])。

ArrayList主要方法源码剖析 增加
public boolean add(E e) {
    //扩容判断
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

public void add(int index, E element) {
    //判断index是否越界,错误产生IndexOutOfBoundsException
    rangeCheckForAdd(index);
    //进行扩容检查
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //对数组进行复制,将空出的Index位置出入element,并将index后的所有数据后移一个位置。
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //将index上的数据设置为element
    elementData[index] = element;
    //容量+1
    size++;
}
删除
public E remove(int index) {
    //边界检查
    rangeCheck(index);
        
    modCount++;
    //oldValue即要删除的元素
    E oldValue = elementData(index);
    //要复制的元素
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //释放最后一个元素
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

public boolean remove(Object o) {
    //对o进行判断
    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;
            }
    }
    return false;
}
更新
public E set(int index, E element) {
    //数组扩容
    rangeCheck(index);
    //获取要更新的位置的数据
    E oldValue = elementData(index);
    //更新元素
    elementData[index] = element;
    return oldValue;
}
总结 1.ArrayList底层是通过数组来保存数据的。默认的容量是10. 2.JDK1.8中,ArrayList扩容使用位运算newCapacity = oldCapacity + (oldCapacity >> 1) 3.ArrayList实现java.io.Serializable的方式。当写入到输出流时,先写入“容量”,再依次写入“每一个元素”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67665.html

相关文章

  • Java集合总结【面试题+脑图】,将知识点网打尽!

    摘要:而在集合中,值仅仅是一个对象罢了该对象对本身而言是无用的。将这篇文章作为集合的总结篇,但觉得没什么好写就回答一些面试题去了,找了一会面试题又觉得不够系统。 前言 声明,本文用的是jdk1.8 花了一个星期,把Java容器核心的知识过了一遍,感觉集合已经无所畏惧了!!(哈哈哈....),现在来总结一下吧~~ 回顾目录: Collection总览 List集合就这么简单【源码剖析】 Ma...

    yearsj 评论0 收藏0
  • 文掌握关于Java数据结构所有知识点(欢迎起完善)

    摘要:是栈,它继承于。满二叉树除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。没有键值相等的节点。这是数据库选用树的最主要原因。 在我们学习Java的时候,很多人会面临我不知道继续学什么或者面试会问什么的尴尬情况(我本人之前就很迷茫)。所以,我决定通过这个开源平台来帮助一些有需要的人,通过下面的内容,你会掌握系统的Java学习以及面试的相关知识。本来是想通过Gitbook的...

    keithxiaoy 评论0 收藏0
  • Week 2 - Java 容器 - 详细剖析 List 之 ArrayList, Vector,

    摘要:底层使用的是双向链表数据结构之前为循环链表,取消了循环。快速随机访问就是通过元素的序号快速获取元素对象对应于方法。而接口就是用来标识该类支持快速随机访问。仅仅是起标识作用。,中文名为双端队列。不同的是,是线程安全的,内部使用了进行同步。 前言 学习情况记录 时间:week 2 SMART子目标 :Java 容器 记录在学习Java容器 知识点中,关于List的需要重点记录的知识点。...

    MartinDai 评论0 收藏0
  • 集合框架源码学习之ArrayList

    摘要:用户自己指定容量创建大小的数组创建空数组默认构造函数,其默认初始容量为构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。以正确的顺序返回该列表中的元素的迭代器。此方法充当基于阵列和基于集合的之间的桥梁。 目录: 0-0-1. 前言 0-0-2. 集合框架知识回顾 0-0-3. ArrayList简介 0-0-4. ArrayList核心源码 0-0-5. Ar...

    BLUE 评论0 收藏0
  • List集合就这么简单【源码剖析

    摘要:线程不安全底层数据结构是链表。的默认初始化容量是,每次扩容时候增加原先容量的一半,也就是变为原来的倍删除元素时不会减少容量,若希望减少容量则调用它不是线程安全的。 前言 声明,本文用得是jdk1.8 前一篇已经讲了Collection的总览:Collection总览,介绍了一些基础知识。 现在这篇主要讲List集合的三个子类: ArrayList 底层数据结构是数组。线程不安全 ...

    cpupro 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<