Java进阶学习之不可变集合

在 Java 编程中,不可变集合是一种非常重要的概念,它提供了诸多优势,如线程安全、可靠性和性能优化等。下面将从多个方面详细介绍 Java 中的不可变集合。

一、不可变集合的特点

不可变集合是指创建后其内容不能被修改的集合。一旦创建,就不能添加、删除或修改集合中的元素。这种特性使得不可变集合在多线程环境下是线程安全的,因为没有并发修改的风险。

  1. 线程安全:由于不可修改,天然线程安全,无需额外同步。
  2. 内存效率:多个引用可以共享同一个不可变集合实例。
  3. 防御性编程:防止意外修改,确保数据完整性。
  4. 作为常量:适合作为API返回值或配置常量。

不可变集合的局限性

  • 创建后无法修改:如果需要动态修改集合内容,不可变集合就不适用,需要使用可变集合。
  • 创建成本:在某些情况下,创建不可变集合可能需要额外的资源和时间,特别是当集合元素较多时。

二、不可变集合的创建方法对比

方法 版本要求 特点
List.of() Java 9+ 简洁,支持可变参数,快速创建不可变List
Set.of() Java 9+ 简洁,支持可变参数,快速创建不可变Set
Map.of()/Map.ofEntries() Java 9+ 简洁,支持键值对创建不可变Map
Collections.unmodifiableXXX() Java 1.2+ 创建视图,依赖原始集合,不推荐用于创建真正的不可变集合
Guava的ImmutableList等 任意 需要额外依赖,提供更多不可变集合实现

1.Java 8 及以前的实现方式

在 Java 8 及以前,可以使用 Collections 工具类的静态方法来创建不可变集合。

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

public class ImmutableCollectionBeforeJava9 {
    public static void main(String[] args) {
        // 创建一个普通的可变列表
        List<String> mutableList = new ArrayList<>();
        mutableList.add("apple");
        mutableList.add("banana");
        mutableList.add("cherry");

        // 使用 Collections.unmodifiableList() 方法创建不可变列表
        List<String> immutableList = Collections.unmodifiableList(mutableList);

        try {
            // 尝试修改不可变列表,会抛出 UnsupportedOperationException
            immutableList.add("date");
        } catch (UnsupportedOperationException e) {
            System.out.println("不能修改不可变列表:" + e.getMessage());
        }

        // 注意:如果修改原始的可变列表,不可变列表也会受到影响
        mutableList.add("elderberry");
        System.out.println("不可变列表:" + immutableList);
    }
}

2.Java 9 及以后的实现方式

Java 9 引入了一系列工厂方法来创建不可变集合,使用起来更加简洁。

java 复制代码
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ImmutableCollectionJava9 {
    public static void main(String[] args) {
        // 创建不可变列表
        List<String> immutableList = List.of("apple", "banana", "cherry");

        // 创建不可变集合
        Set<String> immutableSet = Set.of("apple", "banana", "cherry");

        // 创建不可变映射
        Map<String, Integer> immutableMap = Map.of("apple", 1, "banana", 2, "cherry", 3);

        try {
            // 尝试修改不可变列表,会抛出 UnsupportedOperationException
            immutableList.add("date");
        } catch (UnsupportedOperationException e) {
            System.out.println("不能修改不可变列表:" + e.getMessage());
        }

        try {
            // 尝试修改不可变集合,会抛出 UnsupportedOperationException
            immutableSet.add("date");
        } catch (UnsupportedOperationException e) {
            System.out.println("不能修改不可变集合:" + e.getMessage());
        }

        try {
            // 尝试修改不可变映射,会抛出 UnsupportedOperationException
            immutableMap.put("date", 4);
        } catch (UnsupportedOperationException e) {
            System.out.println("不能修改不可变映射:" + e.getMessage());
        }
    }
}

三. 与可变集合的转换

如果需要将不可变集合转换为可变集合,可以通过创建一个新的可变集合并将不可变集合的元素复制到其中。

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

public class ConvertImmutableToMutable {
    public static void main(String[] args) {
        // 创建不可变列表
        List<String> immutableList = List.of("apple", "banana", "cherry");

        // 将不可变列表转换为可变列表
        List<String> mutableList = new ArrayList<>(immutableList);

        // 可以对可变列表进行修改
        mutableList.add("date");
        System.out.println("可变列表:" + mutableList);
    }
}

五、使用场景

  1. 配置信息:程序启动时加载的配置
  2. 常量集合:如国家/地区代码等
  3. 多线程共享数据:避免并发修改问题
  4. API返回值:防止调用方修改内部数据
  5. 函数参数:确保函数不会修改输入集合

六、注意事项

  1. Collections.unmodifiableXXX方法返回的是视图,原始集合的修改会反映到视图
  2. Java 9+的of()方法创建的集合是真正不可变的
  3. 尝试修改不可变集合会抛出UnsupportedOperationException
  4. 不可变集合可以包含null元素(Java 9+的of()方法不允许)
java 复制代码
List<String> list = List.of("a", "b", "c");
list.add("d"); // 抛出UnsupportedOperationException

Set<Integer> set = Set.of(1, 2, 3);
set.remove(1); // 抛出UnsupportedOperationException

Map<String, Integer> map = Map.of("one", 1);
map.put("two", 2); // 抛出UnsupportedOperationException

掌握不可变集合的使用,可以显著提高Java程序的健壮性和安全性,特别是在多线程环境下。

相关推荐
代码山河6 小时前
Python就业方向全解析:后端、爬虫、数据分析、AI你选哪个?
python·学习·架构·教程·面向对象·项目
学长毕设6 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
释厄6237 小时前
AI学习层更新与跨体转移律——人类社会同构·不缺理解缺 U
人工智能·学习
zzu123zsw7 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号8 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
论文复现现场9 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠9 小时前
泛微OAe9-后端二开异步调用
java
秋天的怀念_David10 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放10 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog10 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友