ArrayList的动态扩容机制源码分析

发布时间:2022-10-14 09:05:43 作者:iii
来源:亿速云 阅读:149

ArrayList的动态扩容机制源码分析

目录

  1. 引言
  2. ArrayList的基本结构
  3. ArrayList的初始化
  4. ArrayList的添加操作
  5. ArrayList的动态扩容机制
  6. ArrayList的扩容策略
  7. ArrayList的扩容性能分析
  8. ArrayList的缩容机制
  9. ArrayList的线程安全性
  10. 总结

引言

ArrayList 是 Java 集合框架中最常用的数据结构之一,它基于动态数组实现,提供了高效的随机访问和动态扩容能力。本文将深入分析 ArrayList 的动态扩容机制,探讨其源码实现、扩容策略、性能影响以及相关的优化策略。

ArrayList的基本结构

ArrayList 的内部结构主要由以下几个部分组成:

transient Object[] elementData; // 存储元素的数组
private int size; // 当前元素数量
protected transient int modCount = 0; // 结构修改次数

ArrayList的初始化

ArrayList 提供了多个构造函数,允许用户指定初始容量或使用默认容量。

// 默认构造函数,初始容量为10
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 指定初始容量的构造函数
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(Collection<? extends E> 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;
    }
}

ArrayList的添加操作

ArrayList 的添加操作主要通过 add(E e) 方法实现。该方法首先检查当前数组是否已满,如果已满则触发扩容操作,然后将新元素添加到数组的末尾。

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

ArrayList的动态扩容机制

5.1 扩容触发条件

ArrayList 的扩容操作在以下情况下触发:

5.2 扩容过程

ArrayList 的扩容过程主要通过 grow(int minCapacity) 方法实现。该方法首先计算新的容量,然后创建一个新的数组并将原数组中的元素复制到新数组中。

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);  // 新容量为原容量的1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);  // 复制元素到新数组
}

5.3 扩容后的元素复制

在扩容过程中,Arrays.copyOf() 方法用于将原数组中的元素复制到新数组中。该方法内部调用了 System.arraycopy(),这是一个本地方法,具有较高的性能。

public static <T> T[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    return copy;
}

ArrayList的扩容策略

6.1 默认扩容策略

ArrayList 的默认扩容策略是将容量扩大为原来的 1.5 倍。这种策略在大多数情况下能够平衡内存使用和性能。

int newCapacity = oldCapacity + (oldCapacity >> 1);  // 新容量为原容量的1.5倍

6.2 自定义初始容量

用户可以通过构造函数指定 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);
    }
}

6.3 扩容策略的优化

在某些场景下,用户可以通过手动调整 ArrayList 的容量来优化性能。例如,在已知需要添加大量元素时,可以提前调用 ensureCapacity(int minCapacity) 方法,避免频繁扩容。

public void ensureCapacity(int minCapacity) {
    if (minCapacity > elementData.length) {
        grow(minCapacity);
    }
}

ArrayList的扩容性能分析

7.1 时间复杂度分析

ArrayList 的扩容操作的时间复杂度为 O(n),其中 n 是当前数组的长度。这是因为在扩容过程中需要将原数组中的元素复制到新数组中。

7.2 空间复杂度分析

ArrayList 的扩容操作的空间复杂度为 O(n),其中 n 是新数组的长度。这是因为在扩容过程中需要创建一个新的数组来存储元素。

7.3 扩容对性能的影响

频繁的扩容操作会导致性能下降,因为每次扩容都需要复制大量元素。因此,在实际使用中,应尽量避免频繁扩容,可以通过提前设置合适的初始容量或手动调整容量来优化性能。

ArrayList的缩容机制

ArrayList 并没有提供自动缩容的机制,但用户可以通过调用 trimToSize() 方法手动将 ArrayList 的容量调整为当前元素数量,从而减少内存占用。

public void trimToSize() {
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

ArrayList的线程安全性

ArrayList 不是线程安全的,如果在多线程环境下使用 ArrayList,可能会导致数据不一致或其他并发问题。为了在多线程环境下使用 ArrayList,可以使用 Collections.synchronizedList() 方法将其包装为线程安全的列表。

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());

总结

ArrayList 的动态扩容机制是其高效性能的关键之一。通过分析其源码,我们了解了 ArrayList 的扩容触发条件、扩容过程、扩容策略以及性能影响。在实际使用中,合理设置初始容量、避免频繁扩容以及手动调整容量是优化 ArrayList 性能的重要手段。同时,ArrayList 的线程安全性问题也需要在使用时特别注意。

推荐阅读:
  1. Arraylist动态扩容详解
  2. ArrayList的源码分析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

arraylist

上一篇:win10预览版绿屏怎么解决

下一篇:Nebula Graph和SpringBoot环境连接和查询怎么实现

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》