深入分析ArrayList数据结构:源码和扩容机制

目录

前言

一、ArrayList源码分析

定义

成员变量

构造函数 

方法 

添加元素

删除元素

二、扩容机制分析

总结


前言

ArrayList是Java中常用的数据结构之一,它实现了List接口,可以动态地增加或删除元素。在实际开发中,我们经常使用ArrayList来存储和操作数据。本文将从源码和扩容机制两个方面来详细介绍ArrayList的实现原理。

一、ArrayList源码分析

ArrayList的源码位于java.util包中,它是一个数组实现的动态列表。在ArrayList中,元素是按照插入顺序存储的,可以根据索引访问元素。

定义

ArrayList定义如下:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

可以看到,ArrayList实现了List接口,并继承了AbstractList抽象类。其中,List接口定义了大部分操作方法,而AbstractList提供了默认的实现。

  • RandomAccess 是一个标志接口,表明实现这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
  • ArrayList 实现了 Cloneable 接口 ,即覆盖了函数clone(),能被克隆。
  • ArrayList 实现了 java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

成员变量

ArrayList的成员变量包括:

在ArrayList中,主要有三个成员变量:

// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;

// 存储元素的数组
private transient Object[] elementData;

// 元素的数量
private int size;

可以看到,elementData是存储元素的数组,size是元素的数量。transient关键字表示该成员变量不会被序列化。 

构造函数 

ArrayList有多个构造函数,其中最常用的是无参构造函数和带有初始容量参数的构造函数。

// 指定初始容量
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        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;
}

可以看到,在指定初始容量的构造函数中,如果initialCapacity大于0,则新建一个elementData数组,创建一个指定容量的ArrayList;如果等于0,则使用空数组EMPTY_ELEMENTDATA;否则抛出异常。而在使用无参构造函数创建ArrayList时,实际上初始化赋值的是一个空数组,当真正对数组进行添加元素操作时,才真正分配容量,即向数组中添加第一个元素时,数组容量扩为10。

JDK6 new 无参构造的 ArrayList 对象时,直接创建了长度是 10 的 Object[] 数组 elementData 。

方法 

ArrayList的方法包括:

添加元素

ArrayList提供了多个添加元素的方法,最常用的是以下两个:

// 在末尾添加元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 确保容量足够
    elementData[size++] = e;  // 添加元素
    return true;
}

// 在指定位置插入元素
public void add(int index, E element) {
    rangeCheckForAdd(index);  // 检查下标是否越界
    ensureCapacityInternal(size + 1);  // 确保容量足够
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);  // 移动元素
    elementData[index] = element;  // 插入新元素
    size++;  // 元素数量+1
}

在添加元素时,首先需要确保容量足够,即调用ensureCapacityInternal方法进行判断和扩容操作。然后,根据是在末尾添加元素还是在指定位置插入元素,分别进行添加操作。 

删除元素

ArrayList提供了多个删除元素的方法,最常用的是以下两个:

// 通过下标删除元素
public E remove(int index) {
    rangeCheck(index);  // 检查下标是否越界
    modCount++;  // 修改次数+1
    E oldValue = elementData(index);  // 获取要删除的元素
    int numMoved = size - index - 1;
    if (numMoved > 0) {
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);  // 移动元素
    }
    elementData[--size] = null;  // 将最后一个元素置为null
    return oldValue;  // 返回要删除的元素
}

// 从列表中删除指定元素的第一个出现(如果存在)。 如果列表不包含该元素,则它不会更改。
// 返回true,如果此列表包含指定的元素
public boolean remove(Object 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;
}

二、扩容机制分析

ArrayList的扩容机制是其实现的一个重要特点。在源码中,我们可以看到扩容方法ensureCapacityInternal()和grow()的实现。当添加元素时,如果当前容量不足,则会调用ensureCapacityInternal()方法进行扩容。该方法会先判断当前数组是否为空,如果为空,则将容量设置为默认值10;否则,将容量设置为当前容量的1.5倍。如果扩容后的容量仍然不够,则直接使用所需的容量。如果所需的容量超过了最大容量,则抛出OutOfMemoryError异常。

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    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);
}

在grow()方法中,首先计算新的容量newCapacity,它是原有容量oldCapacity的1.5倍。然后,判断新容量是否足够,如果不够,则使用所需的容量。如果所需的容量超过了最大容量,则抛出OutOfMemoryError异常。最后,使用Arrays.copyOf()方法将原有数组复制到新数组中。


总结

ArrayList是Java中最常用的集合之一,它是一个动态数组,可以自动扩容。在本文中,我们深入分析了ArrayList的实现原理,包括源码和扩容机制。我们了解到,ArrayList的源码包括成员变量、构造函数和方法。ArrayList的扩容机制是在元素数量超过当前容量时,创建一个新的数组,并将原数组中的元素复制到新数组中。新数组的容量是原数组容量的1.5倍。如果新数组的容量小于指定的最小容量,则使用指定的最小容量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值