java打卡学习3:ArrayList扩容机制

ArrayList扩容机制概述

ArrayList是基于动态数组实现的集合类,当元素数量超过当前数组容量时,会自动触发扩容机制。其核心目的是平衡内存占用与性能开销。

默认初始容量

  • 未指定初始容量时,默认创建一个空数组(JDK 1.8+),首次添加元素时扩容至默认容量10
  • 指定初始容量可通过构造函数ArrayList(int initialCapacity),直接初始化对应大小的数组。

扩容触发条件

当调用add()方法添加元素时,若当前元素数量size + 1 > 数组长度,即数组已满,触发扩容。

扩容规则

  1. 计算新容量 :新容量为旧容量的1.5倍(即int newCapacity = oldCapacity + (oldCapacity >> 1))。
  2. 边界检查:若新容量仍不足所需最小容量(如一次性添加多个元素),则直接采用所需容量。
  3. 最大值限制 :新容量不得超过Integer.MAX_VALUE - 8,否则抛出OutOfMemoryError
  4. 数组拷贝 :通过Arrays.copyOf()创建新数组,并将旧数据复制到新数组。

示例代码片段

java 复制代码
// JDK 1.8中的扩容核心逻辑(简化版)
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

性能优化建议

  • 预分配容量:若已知大致元素数量,初始化时指定容量避免多次扩容。
  • 批量操作 :优先使用addAll()而非循环add(),减少扩容次数。

与Vector对比

Vector默认扩容为2倍,且线程安全但性能较低。ArrayList在非线程安全场景下更高效。

前端视角理解:

ArrayList扩容机制与前端概念的类比

ArrayList的扩容机制是指当数组容量不足时,自动创建一个更大的新数组,并将原有元素复制到新数组中。这一机制可以类比到前端开发中的某些场景:

动态DOM元素加载

类似于ArrayList在容量不足时扩容,前端页面在滚动加载更多内容时,会动态创建新的DOM元素并插入到页面中。两者都涉及动态扩展和性能优化。

虚拟列表技术

前端框架(如React/Vue)的虚拟列表仅渲染可视区域的DOM元素,当滚动时动态替换内容。这与ArrayList按需扩容的思路一致,避免一次性占用过多内存。

响应式布局的断点处理

CSS媒体查询在不同屏幕尺寸下应用不同的布局规则,类似于ArrayList根据当前size判断是否需要扩容。两者都是基于阈值触发的动态调整机制。

核心相似点

按需分配原则

ArrayList在add()时检查容量,前端在用户交互时加载资源,均遵循延迟分配的优化策略。

扩容/渲染的性能损耗

ArrayList扩容涉及数组复制,前端DOM操作会触发重排重绘。两者都需要权衡频率与单次操作的代价。

预分配优化

ArrayList可指定初始容量,前端可通过preload/prefetch提前加载资源,减少后续延迟。

差异点

触发时机

ArrayList扩容由API调用直接触发,前端动态加载通常由用户行为(如滚动)间接触发。

扩容粒度

ArrayList通常按固定倍数(如1.5倍)扩容,前端可能按需加载单条数据或分页批量加载。

实际应用示例

javascript 复制代码
// 类似ArrayList扩容的前端懒加载实现
const lazyLoad = (() => {
  let loadedItems = 10; // 初始容量
  const loadMore = () => {
    const newItems = loadedItems * 1.5; // 模拟扩容因子
    renderItems(loadedItems, newItems);
    loadedItems = newItems;
  };
  return { loadMore };
})();

这种类比有助于理解不同领域的容量管理策略,但需注意前端场景通常更强调事件驱动和异步处理特性。

今日练习:

手写简易ArrayList实现

ArrayList是基于数组实现的动态扩容列表,以下是核心功能的简化实现代码(Java版本):

java 复制代码
public class SimpleArrayList<E> {
    private static final int DEFAULT_CAPACITY = 10;
    private Object[] elementData;
    private int size;

    public SimpleArrayList() {
        this.elementData = new Object[DEFAULT_CAPACITY];
    }

    public SimpleArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else {
            throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
        }
    }
}

添加元素方法

java 复制代码
public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (minCapacity - elementData.length > 0) {
        grow(minCapacity);
    }
}

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍扩容
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    elementData = Arrays.copyOf(elementData, newCapacity);
}

查询与删除操作

java 复制代码
public E get(int index) {
    rangeCheck(index);
    return (E) elementData[index];
}

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

private void rangeCheck(int index) {
    if (index >= size || index < 0) {
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    }
}

迭代器实现

java 复制代码
public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    int cursor;       // 当前元素索引
    int lastRet = -1; // 最后返回的元素索引

    public boolean hasNext() {
        return cursor != size;
    }

    public E next() {
        int i = cursor;
        if (i >= size) throw new NoSuchElementException();
        Object[] elementData = SimpleArrayList.this.elementData;
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
}

关键设计要点

  • 动态扩容机制:当数组空间不足时,按原容量的1.5倍扩容(JDK标准实现)
  • 快速随机访问:通过数组下标实现O(1)时间复杂度的元素访问
  • 线程不安全:简易实现不考虑多线程同步问题
  • 泛型支持:通过Object数组存储和类型转换实现泛型特性
  • 空间回收:删除元素时主动置null帮助GC回收

完整实现还应包含size()、isEmpty()、contains()等常用方法,此处展示的是核心逻辑框架。实际JDK中的ArrayList还包含序列化支持、批量操作优化等更复杂的实现。

相关推荐
减瓦几秒前
Jackson 自定义反序列化器的类型不匹配陷阱
java·后端
魔法阵维护师几秒前
从零开发游戏需要学习的c#模块,第三十章(掉落物品 —— 血包与能量)
学习·游戏·c#
在学了加油1 分钟前
Inception v1学习笔记
笔记·python·学习
qq_452396235 分钟前
第九篇:《Dockerfile 指令精讲(二):WORKDIR、ENV、ARG、EXPOSE》
java·开发语言·docker
Cthy_hy6 分钟前
Python算法竞赛:集合去重+字典映射 核心用法一站式整理
数据结构·python·算法
JAVA社区7 分钟前
Java高级全套教程(九)—— SpringCloud超详细实战详解
java·开发语言·后端·spring cloud·面试·职场和发展
索西引擎8 分钟前
【langchain 1.0】ChromaDB 原生 API 实战:为 LangChain 向量库打造管理工具集
python·ai·langchain
是一个Bug8 分钟前
LangChain 入门完全指南:核心概念、学习路线与实战 Demo
学习·langchain
Sirius.z9 分钟前
第J6周:Inception v1算法实战
python
wyjcxyyy9 分钟前
java反序列化-cc1链
java·c语言·开发语言