大家好,我是大华! 很多朋友觉得“内存溢出”是高手才碰得到的问题。什么高并发、都是大流量系统才会遇到的烦恼。 其实很多内存溢出,不是项目大,有时候可能是因为集合用得太野了。

  • 为什么ArrayList能把内存撑爆?
  • HashMap缓存为啥越积越多?
  • 为什么字符串拼接不能用+
  • 大数据处理时,怎么避免一次性加载?

这篇文章来分享10个集合内存管理技巧。

1. ArrayList 别乱用

我一开始写爬虫,抓网页数据,往 ArrayList 里狂塞。

List<String> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
	// 模拟抓数据
    list.add(fetchDataFromWeb()); 
}

跑着跑着就崩了,为啥呢? 因为ArrayList默认初始容量是10。存的第11个,它就得扩容,复制数组,然后重新分配内存。 10万条数据,内存肯定就会蹭蹭往上涨了。

解决办法:

// 估算个数量,直接定大小
List<String> list = new ArrayList<>(100000);

一次到位 场景: 数据量可预估的,比如读文件、批量处理订单。

2. 用完就扔,别让集合赖在内存里

写了个方法,统计某次考试的平均分。

public class ScoreTool {
    private List<Integer> scores = new ArrayList<>();

    public double getAverage() {
        scores.clear();
        // 假设这是从某处读来的成绩
        for (int i = 0; i < 30; i++) {
            scores.add(70 + i); // 模拟成绩 70~100
        }
        return scores.stream().mapToInt(Integer::intValue).average().orElse(0);
    }
}

每次调用getAverage(),都会往scores里塞新数据。 虽然clear()了,但这个scores对象一直存在,GC回收不了。 调用1000 次,就存了1000批数据,内存越来越高。

解决办法:

public double getAverage() {
    List<Integer> scores = new ArrayList<>();
    for (int i = 0; i < 30; i++) {
        scores.add(70 + i);
    }
    return scores.stream().mapToInt(Integer::intValue).average().orElse(0);
    // 方法结束,scores 自动消失,内存回收
}

临时数据用局部变量,用完就走,就不占地方。

3. 别一次性读大文件

你想读一个存了1万个名字的txt文件。别这么写:

List<String> allNames = Files.readAllLines(Paths.get("names.txt")); // 全读进来

万一文件特别大,内存直接爆。

正确做法:

一行一行读,处理完就扔。

try (BufferedReader reader = Files.newBufferedReader(Paths.get("names.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println("处理:" + line);
        // 处理完就不管了,不用存起来
    }
}

就像吃西瓜,一口一口吃,别整个塞嘴里。

4. 去重?用Set

你要从一堆名字里去掉重复的。别这么写:

List<String> unique = new ArrayList<>();
String[] names = {"张三", "李四", "张三", "王五"};

for (String name : names) {
    if (!unique.contains(name)) { // 每次都要从头找一遍
        unique.add(name);
    }
}

contains要一个个比,数据变多时会很慢。 一行解决:

Set<String> unique = new HashSet<>(Arrays.asList(names));

Set本身就是不重复的,直接去重,又快又省事。

5. 拼字符串,用StringBuilder

你想把几个单词拼成一句话。 别这么干:

String sentence = "";
String[] words = {"我", "是", "大", "华"};
for (String word : words) {
    sentence += word; // 每次都新建一个字符串
}

Java的字符串是“ immutable ”(不可变),+=实际是不断创建新对象,内存爆炸。

正确写法:

StringBuilder sb = new StringBuilder();
for (String word : words) {
    sb.append(word);
}
String sentence = sb.toString();

StringBuilder就像一个可伸缩的盒子,往里塞东西不换盒子,更加省内存。

6. 数字别new,直接用

Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println(a == b); // false!两个不同的对象

你new了两个 10,它们是两个不同的“盒子”,只是装的东西一样。

推荐写法:

Integer a = 10;
Integer b = 10;
System.out.println(a == b); // true!同一个“盒子”

Java会缓存 -128 到 127 的数字,直接用更省内存。

7. 用EnumSet记状态

比如你定义了考试状态:PASSED, FAILED, PENDING。 你想记哪些状态已经处理过了。别用HashSet

Set<Status> processed = new HashSet<>();
processed.add(Status.PASSED);

改用:

Set<Status> processed = EnumSet.noneOf(Status.class);
processed.add(Status.PASSED);

EnumSet内部用二进制位表示,一个int就能存32个状态,超级省内存。

8. 转数组,记得说清楚类型

List<String> list = Arrays.asList("a", "b", "c");
Object[] arr = list.toArray(); // 得到 Object 数组
String[] strArr = (String[]) arr; // 强转可能出错

安全写法:

String[] arr = list.toArray(new String[0]);

告诉系统:我要的是 String 数组,别给我 Object。

9. 读文件后,记得关

// 错误示范:忘了关!
BufferedReader reader = new BufferedReader(new FileReader("names.txt"));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println("名字: " + line + ", 长度: " + line.length());
}
// 没有 reader.close();

正确做法: Java有个超好用的语法:try-with-resources 它能保证:只要用它打开的资源,用完自动关,哪怕中间出错也不怕。

try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} // 自动关闭

关键点:

  • BufferedReader写在try (...)
  • 它实现了AutoCloseable接口,系统知道它能自动关
  • 大括号一结束,自动调close()
  • 即使中间报错,也会关

总结

  1. ArrayList提前定大小
  2. 临时数据用局部变量
  3. 临时缓存用WeakHashMap
  4. 大文件别全读
  5. 去重用Set
  6. 拼接用StringBuilder
  7. Integernew
  8. 枚举用EnumSet
  9. 转数组说清类型
  10. 流用完记得关

赶紧改改,让你的Java程序又快又稳!

?往期精彩

《Elasticsearch 太重?来看看这个轻量级的替代品 Manticore Search》

《只会写 Mapper 就敢说会 MyBatis?面试官:原理都没懂》

《别学23种了!Java项目中最常用的6个设计模式,附案例》

《写给小公司前端的 UI 规范:别让页面丑得自己都看不下去》

《Vue3+TS设计模式:5个真实场景让你代码更优雅》

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:[email protected]