Java中有100万个对象,用list map泛型存储和用list对象泛型存储,那个占用空间大,为什么...

在 Java 中,使用 List<Map<泛型类型, 泛型类型>> 存储 100 万个对象和使用 List<对象类型> 存储 100 万个对象,通常情况下 List<Map<泛型类型, 泛型类型>> 占用的空间更大。下面详细分析原因:

内存占用情况分析

1. List<对象类型> 存储方式

当使用 List<对象类型> 存储对象时,List 本身会维护一个内部数组来存储这些对象的引用。每个对象会在堆内存中占据一定的空间,而 List 只需要存储这些对象的引用。
登录后复制

java 复制代码
import java.util.ArrayList;
import java.util.List;

class MyObject {
    private int id;
    private String name;

    public MyObject(int id, String name) {
        this.id = id;
        this.name = name;
    }
}

public class ListObjectExample {
    public static void main(String[] args) {
        List<MyObject> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            list.add(new MyObject(i, "Name" + i));
        }
    }
}

在这个例子中,List 只需要存储 100 万个 MyObject 对象的引用,而每个 MyObject 对象包含一个 int 类型的 id 和一个 String 类型的 name

2. List<Map<泛型类型, 泛型类型>> 存储方式

当使用 List<Map<泛型类型, 泛型类型>> 存储对象时,List 同样会维护一个内部数组来存储 Map 对象的引用。而每个 Map 对象本身也需要占用一定的内存空间,并且 Map 内部会存储键值对,每个键值对也会占用额外的内存。
登录后复制

java 复制代码
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ListMapExample {
    public static void main(String[] args) {
        List<Map<String, Object>> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", i);
            map.put("name", "Name" + i);
            list.add(map);
        }
    }
}

在这个例子中,List 需要存储 100 万个 Map 对象的引用,每个 Map 对象内部又存储了键值对,除了存储与 MyObject 相同的数据外,Map 本身还有额外的开销,例如哈希表的数组、链表节点等。

占用空间大的原因总结

  • 额外的数据结构开销Map 是一种复杂的数据结构,为了实现高效的查找、插入和删除操作,Map 内部使用了哈希表等数据结构,这些数据结构需要额外的内存空间来存储。例如,HashMap 内部会维护一个数组和链表(或红黑树)来解决哈希冲突,这些都会增加内存占用。
  • 键值对的包装开销 :在 Map 中,每个键值对都需要封装成一个 Entry 对象,这也会增加额外的内存开销。

测试类

登录后复制

java 复制代码
public class MemoryUsageTest {

    // 定义一个简单的对象类
    static class MyObject {
        private int id;
        private String name;

        public MyObject(int id, String name) {
            this.id = id;
            this.name = name;
        }
    }

    // 计算 List<对象类型> 存储方式的内存占用
    public static long testListOfObjects() {
        // 获取当前 JVM 的可用内存
        Runtime runtime = Runtime.getRuntime();
        long beforeMemory = runtime.totalMemory() - runtime.freeMemory();

        // 创建 100 万个 MyObject 对象并存储在 List 中
        List<MyObject> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            list.add(new MyObject(i, "Name" + i));
        }

        // 获取创建对象后的 JVM 可用内存
        long afterMemory = runtime.totalMemory() - runtime.freeMemory();
        // 计算内存占用
        return afterMemory - beforeMemory;
    }

    // 计算 List<Map<泛型类型, 泛型类型>> 存储方式的内存占用
    public static long testListOfMaps() {
        // 获取当前 JVM 的可用内存
        Runtime runtime = Runtime.getRuntime();
        long beforeMemory = runtime.totalMemory() - runtime.freeMemory();

        // 创建 100 万个 Map 对象并存储在 List 中
        List<Map<String, Object>> list = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", i);
            map.put("name", "Name" + i);
            list.add(map);
        }

        // 获取创建对象后的 JVM 可用内存
        long afterMemory = runtime.totalMemory() - runtime.freeMemory();
        // 计算内存占用
        return afterMemory - beforeMemory;
    }

    public static void main(String[] args) {
        // 测试 List<对象类型> 的内存占用
        long listOfObjectsMemory = testListOfObjects();
        System.out.println("List<MyObject> 占用的内存: " + listOfObjectsMemory / 1024 / 1024 + " MB");

        // 测试 List<Map<泛型类型, 泛型类型>> 的内存占用
        long listOfMapsMemory = testListOfMaps();
        System.out.println("List<Map<String, Object>> 占用的内存: " + listOfMapsMemory / 1024 / 1024 + "  MB");

        // 比较两种存储方式的内存占用
        if (listOfMapsMemory > listOfObjectsMemory) {
            System.out.println("List<Map<String, Object>> 占用的内存更大。");
        } else if (listOfMapsMemory < listOfObjectsMemory) {
            System.out.println("List<MyObject> 占用的内存更大。");
        } else {
            System.out.println("两种存储方式占用的内存相同。");
        }
    }
}
运行结果

100万个对象运行3次的结果

第一次

第二次

第三次

相关推荐
geovindu6 分钟前
java: framework from BLL、DAL、IDAL、MODEL、Factory using postgresql 17.0
java·开发语言·postgresql
缘来是黎17 分钟前
Python 进阶:生成器与上下文管理器
java·前端·python
m0_7482402519 分钟前
【Spring Boot】统一数据返回
java·spring boot·后端
陈老师还在写代码22 分钟前
介绍下SpringBoot常用的依赖项
java·spring boot·后端
程序猿零零漆24 分钟前
《从入门到精通:蓝桥杯编程大赛知识点全攻略》(十一)-回文日期、移动距离、日期问题
java·算法·蓝桥杯
007php0071 小时前
Docker、Ollama、Dify 及 DeepSeek 安装配置与搭建企业级本地私有化知识库实践
运维·服务器·开发语言·后端·docker·容器·云计算
众乐乐_20081 小时前
JVM栈帧中|局部变量表、操作数栈、动态链接各自的任务是什么?
java·开发语言·jvm
菜鸟单飞1 小时前
一键查看电脑各硬件详细信息 轻松查看电脑硬件参数
windows·电脑
魏翾蒨1 小时前
VBA语言的数据可视化
开发语言·后端·golang
致奋斗的我们2 小时前
项目:利用rsync备份全网服务器数据
linux·运维·服务器·开发语言·github·rsync·openeuler