java原生数组长度不可变,动态扩容本质是创建新数组并迁移有效数据;需前置判断扩容时机(size+1>length)、合理计算新容量(如×1.5)、按size精确复制、同步更新引用与状态,缩容应筛选重建而非简单截尾。

Java 原生数组长度不可变,这是语言层面的硬性约束——一旦 new int[5],就永远是 5 个格子。所谓“动态扩容”,不是改变它,而是用一个更大的新数组,把有效数据搬过去,再让引用指向新数组。关键不在“扩”,而在“换得准、搬得稳、管得住”。
扩容触发点必须前置判断
不能等填满才动,否则下一次 add 必然越界。正确时机是:准备塞第 size + 1 个元素时,检查是否超出当前物理容量:
- ✅ 正确写法:if (size + 1 > array.length) resize();
- ❌ 错误写法:if (size == array.length) resize(); —— 此时已无空位,resize 后仍会立即再次触发
- 注意区分:size 是当前有效元素个数,array.length 是堆中分配的总格子数,二者语义不同,不可混用
新容量计算要兼顾效率与内存
盲目翻倍或只加 1 都不适用生产场景:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- × 翻倍(newLength = old * 2):小数组时浪费严重(如从 5 扩到 10,实际只用 6)
- × 加 1(newLength = old + 1):高频写入下频繁扩容,时间复杂度退化为 O(n²)
- ✓ 推荐策略:int newCapacity = oldCapacity + (oldCapacity >> 1);(即 ×1.5)
- 兜底保障:若计算结果 ≤ 0 或溢出,直接设为所需最小值(如批量导入预估 5000 条,就设 newCapacity = 5000)
复制必须按有效长度操作
扩容后新数组变长,但旧数据只占前 size 个位置。遍历整个新数组会输出大量默认值(0、null),干扰日志或下游解析:
- ✅ 正确复制:System.arraycopy(oldArray, 0, newArray, 0, size);
- ✅ 简化写法:newArray = Arrays.copyOf(oldArray, newCapacity);(底层仍是 arraycopy)
- ⚠️ 避免 for 循环逐个赋值:性能差、边界易错、JIT 无法优化
- 复制后务必同步更新所有关联状态:比如将 array = newArray;,并确认 size 和 capacity 字段已反映最新情况
缩容不是截尾,而是筛选重建
缩容目标不是简单删掉末尾,而是保留需要的数据、丢弃冗余部分:
- 先确定哪些元素要保留(例如标记有效区间、过滤条件)
- 创建容量恰好等于保留数量的新数组
- 只搬运需保留的元素(可用 arraycopy 分段,也可遍历+条件判断)
- 推荐工具:Arrays.copyOf(arr, newSize) 截掉末尾;Arrays.copyOfRange(arr, from, to) 提取子区间
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










