[数据结构]-ArrayList

一、简介

ArrayList底层其实为一个一维数组,作为数组,ArrayList自然具备数组的特点:

  1. ArrayList元素在物理和逻辑上都是顺序的,即内存地址连续且存储有序;
  2. ArrayList元素必须为同一种类型;
  3. ArrayList获取元素得时间复杂度为O(1),插入、删除的时间复杂度为O(n)。

二、ArrayList 和 LinkedList 的区别

  1. 底层数据结构不同:
    ArrayList基于数组实现,具有连续的内存空间。
    LinkedList基于双向链表实现,每个结点都包含数据以及指向前后结点的引用。
  2. 随机访问元素效率不同:
    ArrayList因为基于数组实现,所以可以通过数组下标访问元素,时间复杂度为O(1)。
    LinkedList不支持随机访问,需要从头或尾遍历才能访问指定位置的元素,时间复杂度为O(n)。
  3. 插入和删除的效率不同:
    ArrayList在中间位置插入或删除元素需要移动其他元素,性能较差,时间复杂度为O(n)。
    LinkedList在任意位置插入或删除元素只需修改相邻节点的引用,性能较好,时间复杂度为O(1)。
  4. 内存占用不同:
    ArrayList需要预先分配一定容量的连续内存空间,会浪费未使用的内存。
    LinkedList每个节点都需要额外存储前后节点的引用,会带来更高的内存开销。

三、实现思路

3.1 基本设计

数组是一个固定的、连续的、线性的数据结构,那么想把它作为一个自动扩展容量的数组列表,则需要做一些扩展。

 	/**
     * 默认初始化空间
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 空元素
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * ArrayList 元素数组缓存区
     */
    transient Object[] elementData;

    /**
     * List 集合元素数量
     */
    private int size;
  1. 在初始化 ArrayList 时,我们可以显示的指定它的初始容量,如果不指定大小 ArrayList 会默认给数组初始化一个默认大小。
  2. 初始化默认大小的时机是哪呢?通过观察源码,我们不难发现:初始化默认大小并不是在 new ArrayList() 这个无参构造方法之中,而是在 add 方法,添加元素时初始化默认大小。
  3. 随着 ArrayList 的元素增加,不可避免的需要增加容量,这时也是 add 方法中进行扩容,同时还得需要把旧数据迁移到新的数组上。所以数据的迁移算是一个比较耗时的操作

3.2 添加元素

简易的 add 方法:

 public boolean add(E e) {
        // 1.保证容量
        int minCapacity = size + 1;
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(minCapacity, DEFAULT_CAPACITY);
        }
        // 判断扩容操作
        if (minCapacity - elementData.length > 0) {
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity < minCapacity) {
                newCapacity = minCapacity;
            }
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
        elementData[size++] = e;
        return true;
    }

3.3 移除元素

ArrayList 的重点离不开对 System.arraycopy 的使用,它是一个本地方法,可以让你从原数组的特定位置,迁移到新数组的指定位置和迁移数量。如图 3-1 所示:
图3-1

图 3-1

移除元素代码:

  public E remove(int index) {
        E oldValue = (E) elementData[index];
        int numMoved = size - 1 - index;
        if (numMoved > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        }
        elementData[--size] = null;
        return oldValue;
    }

四、关于 ArrayList 常见的面试题

  1. 数据结构中有哪些是线性表数据结构?
  2. 数组的元素删除和获取,时间复杂度是多少?
  3. ArrayList 中默认的初始化长度是多少?
  4. ArrayList 中扩容的范围是多大一次?
  5. ArrayList 是如何完成扩容的,System.arraycopy 各个入参的作用是什么?
  6. LinkedList插入速度比一定比ArrayList快吗?

欢迎大家一起在评论区讨论上述问题

(Tips:数据结构系列的源码已经更新到个人仓库地址:个人GitHub地址:https://github.com/Jing1558
个人Gitee地址:https://gitee.com/hzj072214/projects 文章中的代码实现都可以在个人仓库中找到源码,如果觉得有帮助请star)

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值