JAVA9中的不可变集合

董翰池
2023-12-01

不可变集合

Oracle 公司引入一些方便使用的工厂方法,用于创建不可变集合 List,Set,Map 和 Map.Entry 对象。这些高效实用的方法可用来创建空或者非空集合对象。
在 Java SE 8 和更早版本中,我们常用类似 unmodifiableXXX 的集合类方法创建不可变集合对象。举个例子,比如我们想创建一个不可变的 List 对象,可能使用到Collections.unmodifiableList 方法。
然而,这些 Collections.unmodifiableXXX 方法显得非常冗长乏味。为了克服这些缺陷,Oracle 公司给 List、Set 和 Map 接口分别添加了两个更加实用的方法。

List 和 Set 接口使用 of() 方法创建一个空或者非空的不可变 List 或 Set 对象,如:
空 List 示例
List immutableList = List.of();

非空 List 示例
List immutableList = List.of(“one”,“two”,“three”);

Map 分别有两个方法用于创建不可变 Map 对象和不可变 Map.Entry 对象:of() 和 ofEntries()。

空 Map 示例
jshell> Map emptyImmutableMap = Map.of()
emptyImmutableMap ==> {}

非空 Map 示例
jshell> Map nonemptyImmutableMap = Map.of(1, “one”, 2, “two”, 3, “three”)
nonemptyImmutableMap ==> {2=two, 3=three, 1=one}

/**
     * Returns an unmodifiable list containing zero elements.
     *
     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
     *
     * @param <E> the {@code List}'s element type
     * @return an empty {@code List}
     *
     * @since 9
     */
    static <E> List<E> of() {
        return ImmutableCollections.emptyList();
    }

创建不可变的空集合,调用了ImmutableCollections.emptyList方法。
入参是E…时:

/**
     * Returns an unmodifiable list containing an arbitrary number of elements.
     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
     *
     * @apiNote
     * This method also accepts a single array as an argument. The element type of
     * the resulting list will be the component type of the array, and the size of
     * the list will be equal to the length of the array. To create a list with
     * a single element that is an array, do the following:
     *
     * <pre>{@code
     *     String[] array = ... ;
     *     List<String[]> list = List.<String[]>of(array);
     * }</pre>
     *
     * This will cause the {@link List#of(Object) List.of(E)} method
     * to be invoked instead.
     *
     * @param <E> the {@code List}'s element type
     * @param elements the elements to be contained in the list
     * @return a {@code List} containing the specified elements
     * @throws NullPointerException if an element is {@code null} or if the array is {@code null}
     *
     * @since 9
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    static <E> List<E> of(E... elements) {
        switch (elements.length) { // implicit null check of elements
            case 0:
                return ImmutableCollections.emptyList();
            case 1:
                return new ImmutableCollections.List12<>(elements[0]);
            case 2:
                return new ImmutableCollections.List12<>(elements[0], elements[1]);
            default:
                return new ImmutableCollections.ListN<>(elements);
        }
    }

static List of(E… elements)方法根据传入参数的长度调用ImmutableCollections相对应的方法。

		List12(E e0) {
            this.e0 = Objects.requireNonNull(e0);
            this.e1 = null;
        }

        List12(E e0, E e1) {
            this.e0 = Objects.requireNonNull(e0);
            this.e1 = Objects.requireNonNull(e1);
        }

当传入参数的元素为一个或两个元素时,用了一个重载方法。接下来看ListN<>

static final class ListN<E> extends AbstractImmutableList<E>
            implements Serializable {

        static final List<?> EMPTY_LIST = new ListN<>();

        @Stable
        private final E[] elements;

        @SafeVarargs
        ListN(E... input) {
            // copy and check manually to avoid TOCTOU
            @SuppressWarnings("unchecked")
            E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
            for (int i = 0; i < input.length; i++) {
                tmp[i] = Objects.requireNonNull(input[i]);
            }
            elements = tmp;
        }

        @Override
        public boolean isEmpty() {
            return size() == 0;
        }

        @Override
        public int size() {
            return elements.length;
        }

        @Override
        public E get(int index) {
            return elements[index];
        }

        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            throw new InvalidObjectException("not serial proxy");
        }

        private Object writeReplace() {
            return new CollSer(CollSer.IMM_LIST, elements);
        }
    }

ListN<>方法会先生成一个跟入参长度相同的元素数组,for循环将入参的元素赋值给新建的元素数组,并且对每一个元素判断是否为NULL。如果为NULL则抛出空指针异常。

    /**
     * Checks that the specified object reference is not {@code null}. This
     * method is designed primarily for doing parameter validation in methods
     * and constructors, as demonstrated below:
     * <blockquote><pre>
     * public Foo(Bar bar) {
     *     this.bar = Objects.requireNonNull(bar);
     * }
     * </pre></blockquote>
     *
     * @param obj the object reference to check for nullity
     * @param <T> the type of the reference
     * @return {@code obj} if not {@code null}
     * @throws NullPointerException if {@code obj} is {@code null}
     */
    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

如果对不可变集合中添加元素会怎么样???

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71)
	at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:75)
	at com.fengyue.java11.java11.main(java11.java:10)

会抛出 java.lang.UnsupportedOperationException异常。

 类似资料: