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程序的健壮性和安全性,特别是在多线程环境下。

相关推荐
Jabes.yang1 小时前
Java求职面试: 互联网医疗场景中的缓存技术与监控运维应用
java·redis·spring security·grafana·prometheus·oauth2·互联网医疗
饮浊酒1 小时前
Python学习-----小游戏之人生重开模拟器(普通版)
python·学习·游戏程序
QT 小鲜肉1 小时前
【个人成长笔记】在Ubuntu中的Linux系统安装 anaconda 及其相关终端命令行
linux·笔记·深度学习·学习·ubuntu·学习方法
初级炼丹师(爱说实话版)1 小时前
内存泄漏与内存溢出
java
QT 小鲜肉1 小时前
【个人成长笔记】在Ubuntu中的Linux系统安装实验室WIFI驱动安装(Driver for Linux RTL8188GU)
linux·笔记·学习·ubuntu·学习方法
CryptoRzz1 小时前
越南k线历史数据、IPO新股股票数据接口文档
java·数据库·后端·python·区块链
急急黄豆1 小时前
MADDPG学习笔记
笔记·学习
BullSmall1 小时前
《道德经》第十七章
学习
学Java的bb1 小时前
MybatisPlus
java·开发语言·数据库
讓丄帝愛伱1 小时前
Mybatis Log Free插件使用
java·开发语言·mybatis