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

相关推荐
WarrenMondeville2 小时前
4.Unity面向对象-接口隔离原则
java·unity·接口隔离原则
zb200641202 小时前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
编程之升级打怪2 小时前
用排他锁来实现Python语言的变量值更新
开发语言·python
rrrjqy2 小时前
Java基础篇(二)
java·开发语言
打乒乓球只会抽2 小时前
【无标题】
python
路小雨~2 小时前
Django 学习笔记:从入门到项目开发的完整梳理
笔记·python·学习·django
Mr.45672 小时前
JDK17+Druid+SpringBoot3+ShardingSphere5 多表分库分表完整实践(MySQL+PostgreSQL)
java·数据库·spring boot·mysql·postgresql
tsyjjOvO2 小时前
Spring Boot 入门
java·spring boot·后端
RuoyiOffice2 小时前
SpringBoot+Vue3+Uniapp实现PC+APP双端考勤打卡设计:GPS围栏/内网双模打卡、节假日方案、定时预生成——附数据结构和核心源码讲解
java·spring·小程序·uni-app·vue·产品运营·ruoyi