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还包含序列化支持、批量操作优化等更复杂的实现。

相关推荐
夜瞬17 小时前
NLP学习笔记01:文本预处理详解——从清洗、分词到词性标注
笔记·学习·自然语言处理
m0_3776182318 小时前
Golang怎么连接MySQL数据库_Golang MySQL连接教程【总结】
jvm·数据库·python
-Springer-18 小时前
STM32 学习 —— 个人学习笔记11-1(SPI 通信协议及 W25Q64 简介 & 软件 SPI 读写 W25Q64)
笔记·stm32·学习
LN花开富贵18 小时前
【ROS】鱼香ROS2学习笔记一
linux·笔记·python·学习·嵌入式·ros·agv
weixin_5860614618 小时前
C#怎么通过反射获取类属性_C#如何动态读取元数据【进阶】
jvm·数据库·python
Jurio.18 小时前
本机开发 + 多机执行的极简远端运行工具
linux·git·python·github·远程工作
HoneyMoose18 小时前
Jenkins Cloudflare 部署提示错误
java·servlet·jenkins
阿丰资源18 小时前
基于SpringBoot的物流信息管理系统设计与实现(附资料)
java·spring boot·后端
skywalk816318 小时前
pytest测试的时候这是什么意思?Migrating <class ‘kotti.resources.File‘>
前端·python
Predestination王瀞潞18 小时前
Java EE3-我独自整合(第四章:Spring bean标签的常见配置)
java·spring·java-ee