所有分类
  • 所有分类
  • 未分类

Java的List之坑–Collections#unmodifiableList仍然可变

简介

说明

本文介绍Collections#unmodifiableList的坑:本不可变的集合却被改变了。

Collections#unmodifiableList是保护集合的,让集合不可变,本文介绍它被改变的情况。

Collections#unmodifiableList简介

为了防止 List 集合被误操作,可用Collections#unmodifiableList生成一个不可变(immutable)集合,进行防御性编程。这个不可变集合只能被读取,不能做写操作,包括增加,删除,修改,从而保护不可变集合的安全。

原理:返回的集合没有实现增删改的方法。

示例:

package org.example.a;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Demo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("one", "two", "tree"));
        List<String> unmodifiableList = Collections.unmodifiableList(list);
        // 以下三行都会抛出UnsupportedOperationException 异常
        unmodifiableList.add("four");
        unmodifiableList.remove(1);
        unmodifiableList.set(0, "test");
    }
}

 结果

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.Collections$UnmodifiableCollection.add(Collections.java:1055)
    at org.example.a.Demo.main(Demo.java:12)

问题复现

如果使用不当,Collections#unmodifiableList操作后的List也是可能会被改变的。

代码

package org.example.a;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Demo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("one", "two", "tree"));
        List<String> unmodifiableList = Collections.unmodifiableList(list);
        list.set(0, "one_modified");
        System.out.println(unmodifiableList.get(0));
    }
}

执行结果

one_modified

 可见,被修改了!

原因分析

Collections#unmodifiableList

    public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
    }
    static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                                  implements List<E> {
        private static final long serialVersionUID = -283967356065247728L;

        final List<? extends E> list;

        UnmodifiableList(List<? extends E> list) {
            super(list);
            this.list = list;
        }
        ...
    }

跟前边subList方法一样的情况:新集合底层实际使用(引用)了原始 List

解决方法

法1:JDK1.9的List#of 方法

List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = List.of(list.toArray(new String[]{}));

法2:Guava包的ImmutableList

List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = ImmutableList.copyOf(list);
0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录