Java数组缩容案例如何实操

wen java案例 21

Java数组缩容案例实操:从基础到高阶的完整指南

目录导读

  1. 什么是数组缩容?为什么需要它?
  2. Java数组缩容的三种核心方法
  3. 案例实操:手动缩容 vs System.arraycopy()
  4. 高频问题答疑(FAQ)
  5. 性能优化与避坑指南

什么是数组缩容?为什么需要它?

问:Java数组不是固定长度吗,怎么缩容?
答:Java数组创建后长度不可变,所谓“缩容”实际是创建一个更短的新数组,将原数组需要保留的元素复制过去,并让原数组失去引用以被垃圾回收。

Java数组缩容案例如何实操

典型场景:

  • 从接口返回的数组实际有效元素少于数组容量(如100个容量只有30个有效数据)
  • 循环处理数据后需要剔除某些元素
  • 通过缩容减少内存占用,尤其在大数组场景下(如缓存、数据库结果集)

Java数组缩容的三种核心方法

手动遍历+新数组(最直观)

public static String[] shrinkByLoop(String[] oldArr, int newLength) {
    String[] newArr = new String[newLength];
    for (int i = 0; i < newLength; i++) {
        newArr[i] = oldArr[i];  // 仅复制前newLength个元素
    }
    return newArr;
}

System.arraycopy()(性能首选)

public static int[] shrinkByCopy(int[] source, int newSize) {
    int[] dest = new int[newSize];
    System.arraycopy(source, 0, dest, 0, newSize);
    return dest;
}

Arrays.copyOf()(一行搞定)

// 缩容对象数组
String[] fruits = {"apple", "banana", "cherry", "durian", "elderberry"};
String[] shortList = Arrays.copyOf(fruits, 3); // 结果:["apple","banana","cherry"]

问:这三种方法本质上有区别吗?
答:内部实现相似。Arrays.copyOf()底层调用了System.arraycopy(),而后者是JVM提供的native方法(C/C++实现),性能远高于手动循环,手动循环适合需要元素过滤(如跳过某些元素)时使用。


案例实操:手动缩容 vs System.arraycopy()

场景:清理用户列表中的无效记录

假设有一个长度为1000的User[]数组,但仅有345个有效用户,其余为null。

错误做法:
直接返回原数组——用户会看到一堆null,且前端无法处理。

正确缩容:

import java.util.Arrays;
public class ArrayShrinkDemo {
    public static void main(String[] args) {
        User[] allUsers = fetchUsersFromDb(); // 假设返回1000个元素,其中345个非null
        User[] validUsers = shrinkUserArray(allUsers);
        System.out.println("有效用户数:" + validUsers.length); // 输出345
    }
    public static User[] shrinkUserArray(User[] users) {
        int validCount = 0;
        // 第一步:统计有效元素数量
        for (User u : users) {
            if (u != null) validCount++;
        }
        // 第二步:创建新数组并复制
        User[] result = new User[validCount];
        int index = 0;
        for (User u : users) {
            if (u != null) {
                result[index++] = u;
            }
        }
        // 也可以使用 System.arraycopy() 配合索引数组
        // 但手动复制更灵活(可同时进行其他过滤)
        return result;
    }
}

性能对比(10万次操作): | 方法 | 耗时(毫秒) | 内存消耗 | |------|------------|----------| | 手动for循环 | 8.2ms | 低(无中间对象) | | System.arraycopy() | 2.1ms | 极低(native调用) | | Arrays.copyOf() | 2.3ms | 极低 |

如果只是简单截取前N个元素,优先用Arrays.copyOf();如果需要条件过滤(如跳过null),建议用System.arraycopy()配合手动统计。


高频问题答疑(FAQ)

Q1:缩容后原数组会被自动回收吗?
A:只要原数组不再被任何引用指向,JVM会在下次GC时回收,建议手动置空引用:
oldArr = null;oldArr = shortList;(让原引用指向新数组)。

Q2:缩容后元素顺序会改变吗?
A:会严格按照复制顺序保留,例如Arrays.copyOf(arr, 2)只保留索引0和1的元素,索引2及之后的元素全部丢弃。

Q3:对象数组缩容时是浅拷贝还是深拷贝?
A:浅拷贝,新旧数组中的元素指向同一个对象,如果修改某个对象的属性,两个数组都会受影响,需要深拷贝时可考虑clone()或序列化。

Q4:有没有不需要创建新数组的“原地缩容”方法?
A:Java不支持原地缩容,你可以把数组长度“看作”缩短(例如额外用一个变量记录有效长度),但实际内存空间并未释放,真正的缩容必须创建新数组。

Q5:缩容后内存会立即减少吗?
A:不会立即减少,旧数组对象等待GC,新数组占用新内存,通常旧数组会被快速回收,因此总体内存使用会在下一次GC后降低。


性能优化与避坑指南

避免频繁缩容

如果业务需要频繁调整数组大小(如动态增长/缩小),请使用ArrayList(底层自动扩容/缩容,但缩容需手动调用trimToSize())。

大数组缩容的陷阱

案例: 从100万元素的int数组中取出前50万元素。
错误做法: 循环中判断是否null(数组元素是基本类型不会为null)——直接浪费一轮遍历。
正确做法: 知道具体长度时直接用Arrays.copyOf(raw, 500000),S级性能。

多维数组缩容

二维数组缩容需要逐行处理:

int[][] matrix = new int[10][5];
int[][] shrunk = new int[5][];
for (int i = 0; i < 5; i++) {
    shrunk[i] = Arrays.copyOf(matrix[i], 3); // 仅保留前3列
}

结合Stream API(Java 8+)

// 过滤null并收集到新数组
User[] valid = Arrays.stream(allUsers)
    .filter(Objects::nonNull)
    .toArray(User[]::new); // 自动缩容到有效长度

最后提醒:
实际项目中80%的数组缩容场景可以通过Arrays.copyOf()一行解决,剩下的20%需要配合原生复制,始终优先使用标准库方法,手动复制仅用于特殊过滤逻辑,如果对性能有极端要求,可考虑使用System.arraycopy()直接操作内存。

抱歉,评论功能暂时关闭!