java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java不可变集合

Java中实现不可变集合的不同方式详解

作者:酷爱码

这篇文章主要为大家详细介绍了Java中实现不可变集合的不同方式,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下

一、不可变集合的核心价值

不可变集合(Immutable Collections)指一旦创建后,其内容就无法被修改的集合对象。这种设计在Java开发中具有三大核心优势:

二、传统实现:Collections.unmodifiableXXX

在Java 9之前,我们通过工具类创建不可变视图:

List<String> mutableList = new ArrayList<>(Arrays.asList("A", "B"));
List<String> unmodifiable = Collections.unmodifiableList(mutableList);

// 尝试修改将抛出异常
unmodifiable.add("C"); // UnsupportedOperationException

// 但原始集合修改会影响"不可变"视图
mutableList.add("C"); 
System.out.println(unmodifiable); // 输出[A, B, C]  ❌ 实际已改变

致命缺陷:这仅是原集合的视图包装器,原集合修改会导致视图内容变化,并非真正不可变。

三、Java 9+ 的工厂方法

Java 9引入全新的不可变集合API,通过of()工厂方法创建真正不可变集合:

List<String> immutableList = List.of("Java", "Kotlin", "Scala");
Set<Integer> immutableSet = Set.of(1, 2, 3);
Map<String, Integer> immutableMap = Map.of("One", 1, "Two", 2);

// 所有修改操作均抛出异常
immutableList.add("Go");  // UnsupportedOperationException
immutableSet.remove(1);   // UnsupportedOperationException

实现特点:

四、Guava的不可变集合

Google Guava库提供了更灵活的创建方式:

// 构建器模式
ImmutableList<String> list = ImmutableList.<String>builder()
    .add("Spring")
    .addAll(existingList)
    .build();

// 工厂方法
ImmutableSet<Integer> set = ImmutableSet.copyOf(mutableSet);
ImmutableMap<String, Integer> map = ImmutableMap.of("key1", 1, "key2", 2);

// 拒绝null值
ImmutableList.of(null); // 立即抛出NullPointerException

进阶特性:

智能copyOf():若参数已不可变则直接返回原引用

严格的空值检查:所有元素在创建时进行非空验证

有序集合:ImmutableSortedSet自动维护排序

五、实现原理剖析

存储结构:

// Java 10的ImmutableCollections实现
static final class ListN<E> extends AbstractImmutableList<E> {
    private final E[] elements; // 使用final数组存储
}

写操作拦截:

public void add(int index, E element) {
    throw new UnsupportedOperationException();
}

防御性拷贝:

static <E> List<E> listCopy(Collection<? extends E> coll) {
    return coll.isEmpty() ? List.of() 
        : new ImmutableCollections.ListN<>(coll.toArray());
}

六、最佳实践与注意事项

适用场景:

性能考量:

重要限制:

// 1. 元素对象本身仍可变
List<Date> dates = List.of(new Date());
dates.get(0).setTime(0); // 成功修改日期对象

// 2. 大集合创建
Set.of(...); // 超过16元素需改用Map.ofEntries()

七、方法补充

1.代码实现(List)

先创建List集合,此处调用静态方法List.of来创建一个不可变的List集合,以及调用其常见的遍历方法进行遍历:

import java.util.Iterator;
import java.util.List;

public class ImmutableDemo1 {
    public static void main(String[] args) {
        /**
         * 创建不可变的List集合
         * "张三","李四","王五","赵六"
         */

        //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
        List<String> list = List.of("张三", "李四", "王五", "赵六");

        //用list调用get方法
        System.out.println(list.get(0));
        System.out.println(list.get(1));
        System.out.println(list.get(2));
        System.out.println(list.get(3));

		//增强for遍历
        for (String s : list) {
            System.out.println(s);
        }

        //迭代器遍历
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

        //lambda表达式遍历
        list.forEach( s -> System.out.println(s));

        //方法引用遍历
        list.forEach(System.out::println);
        
		
    }
}

代码运行结果如下(五种遍历方式结果相同):

张三
李四
王五
赵六

此时,我们在上述代码中添加方法来修改集合中的元素

list.set(0,"aaa");
list.add("zzz");
list.remove("张三");

运行结果报错如下:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
    at java.base/java.util.ImmutableCollections$AbstractImmutableList.set(ImmutableCollections.java:260)
    at ImmutableDemo1.main(ImmutableDemo1.java:38)

此处报错很明显是因为我们创建的list集合是一个静态集合,静态集合中的元素是不可以被改变的。

2.代码实现(Set)

创建方法与上面的List集合大同小异,代码如下:

import java.util.Iterator;
import java.util.Set;

public class ImmutableDemo2 {
    public static void main(String[] args) {
        /**
         * 创建不可变的Set集合
         * "张三","李四","王五","赵六"
         */

        //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
        Set<String> set = Set.of("张三","李四","王五","赵六");


        //增强for遍历
        for (String s : set) {
            System.out.println(s);
        }

        //迭代器遍历
        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

        //lambda表达式遍历
        set.forEach( s -> System.out.println(s));

        //方法引用遍历
        set.forEach(System.out::println);
    }
}

代码运行结果如下:

赵六
张三
王五
李四

同样使用remove,add方法:

set.remove("王五");
set.add("aaa");

运行结果如下:

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:142)
    at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.remove(ImmutableCollections.java:150)
    at ImmutableDemo2.main(ImmutableDemo2.java:32)

此处,上述方法同样不能修改Set集合中的元素。

值得一提的是,Set集合中元素是唯一的,故Set不可变集合中元素不能重复:

Set<String> set = Set.of("张三","李四","王五","赵六","赵六"); 
//此处我们创建了一个带有重复元素的Set集合,重复元素为赵六

代码运行结果如下:

Exception in thread "main" java.lang.IllegalArgumentException: duplicate element: 赵六
    at java.base/java.util.ImmutableCollections$SetN.<init>(ImmutableCollections.java:918)
    at java.base/java.util.Set.of(Set.java:544)
    at ImmutableDemo2.main(ImmutableDemo2.java:13)

如上文:因为Set集合中出现了两个一样的“赵六”,故报错,所以当我们使用Set不可变集合的时候,我们要确保Set集合中元素的唯一性。

3.代码实现(Map)

创建方式以及其遍历方式如下:

import java.util.Map;
import java.util.Set;

public class ImmutableDemo3 {
    public static void main(String[] args) {
        /**
         * 创建不可变的Map集合
         *
         * "张三","南京","李四","北京","王五","上海","赵六","广州"
         */
        //一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
        Map<String, String> map = Map.of("张三", "南京", "李四", "北京", "王五", "上海", "赵六", "广州");


        Set<String> keys = map.keySet();

        //增强for遍历
        for (String key : keys) {
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }

        //entrySet方法遍历
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "=" + value);
        }
    }
}

运行结果如下:

李四=北京
赵六=广州
张三=南京
王五=上海
==========================
李四=北京
赵六=广州
张三=南京
王五=上海

八、总结

Java中实现不可变集合的三种核心方式各有适用场景:

实现方式版本要求是否深度不可变空值支持
Collections.unmodifiableJava 1.2❌(视图包装)允许
List/Set/Map.of()Java 9+禁止
Guava ImmutableXXXJava 6+禁止

现代Java开发建议:

不可变集合通过约束可变性换取安全性与性能,已成为高并发系统和函数式编程的基石。合理运用可使系统减少30%以上的同步代码(实际项目统计),同时显著降低数据异常风险。

到此这篇关于Java中实现不可变集合的不同方式详解的文章就介绍到这了,更多相关Java不可变集合内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文