Java基础——不可变集合

1 概述

Java中的List、Set、Map三个接口中,都拥有名为of的静态方法,用于实现不可变集合。
不可变集合集合由于不可变的特性,所以使用是安全的,修改其中的元素会报异常。

2 List接口

API

static <E> List<E> of(E e1) {
    return ImmutableCollections.listFromTrustedArray(e1, e2, e3);
}
...
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
    return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
                                                     e6, e7, e8, e9, e10);
}
static <E> List<E> of(E... elements) {
        switch (elements.length) { // implicit null check of elements
            case 0:
                @SuppressWarnings("unchecked")
                var list = (List<E>) ImmutableCollections.EMPTY_LIST;
                return list;
            case 1:
                return new ImmutableCollections.List12<>(elements[0]);
            case 2:
                return new ImmutableCollections.List12<>(elements[0], elements[1]);
            default:
                return ImmutableCollections.listFromArray(elements);
        }
    }

of方法拥有多个重载,其中参数从1个到10个,还有一个可变长度的重载。为什么不直接使用可变长参数应该是处于性能考虑吧。变长参数会创建一个数组来存储参数,会造成内存的使用和性能的消耗。

List<Integer> list = List.of(1, 2, 4);
for (Integer i:list) {
    System.out.println(i);
}

由于of是List接口的静态方法,有具体实现,返回的是一个List的子类对象,来看看ImmutableCollections.listFromArray方法的返回值。

static <E> List<E> listFromTrustedArray(Object... input) {
    assert input.getClass() == Object[].class;
    for (Object o : input) { // implicit null check of 'input' array
        Objects.requireNonNull(o);
    }

    return switch (input.length) {
        case 0  -> (List<E>) ImmutableCollections.EMPTY_LIST;
        case 1  -> (List<E>) new List12<>(input[0]);
        case 2  -> (List<E>) new List12<>(input[0], input[1]);
        default -> (List<E>) new ListN<>(input, false);
    };
}

返回的是List12或者ListN的实例

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

这两个类都是最终类,无法继承,而两个类中都没有add,set等更改List元素数据的方法。而这两个类又不是抽象类,所以必然在其某个父类中实现了add等方法。

static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
static abstract class AbstractImmutableList<E> extends AbstractImmutableCollection<E>
            implements List<E>, RandomAccess {

    // all mutating methods throw UnsupportedOperationException
    @Override public void    add(int index, E element) { throw uoe(); }
    @Override public boolean addAll(int index, Collection<? extends E> c) { throw uoe(); }
    @Override public E       remove(int index) { throw uoe(); }
    @Override public void    replaceAll(UnaryOperator<E> operator) { throw uoe(); }
    @Override public E       set(int index, E element) { throw uoe(); }
    @Override public void    sort(Comparator<? super E> c) { throw uoe(); }
    ...
}

正好,在其父类AbstractImmutableCollection中就实现了这些方法,而他们的实现都是抛出一个异常。
所以调用上面的这些方法会抛出UnsupportedOperationException异常

2 Set接口

Set几乎和List差不多,只是对于add等方法的拦截在AbstractImmutableCollection抽象类中

static abstract class AbstractImmutableCollection<E> extends AbstractCollection<E> {
    // all mutating methods throw UnsupportedOperationException
    @Override public boolean add(E e) { throw uoe(); }
    @Override public boolean addAll(Collection<? extends E> c) { throw uoe(); }
    @Override public void    clear() { throw uoe(); }
    @Override public boolean remove(Object o) { throw uoe(); }
    @Override public boolean removeAll(Collection<?> c) { throw uoe(); }
    @Override public boolean removeIf(Predicate<? super E> filter) { throw uoe(); }
    @Override public boolean retainAll(Collection<?> c) { throw uoe(); }
}

使用方式:

Set<Integer> set = Set.of(1,4,2);
for (Integer i:set) {
    System.out.println(i);
}

3 Map接口

static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
                               K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
    return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
                                           k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
}

map也提供了一对到十对键值对的of方法,不同的是没有提供跟LIst和Set一样的可变参数,这是因为可变参数的规则限制,可变参数规定参数列表中只能有一个可变参数,而且得位于最后一个参数,要不然传入的实参属于哪个可变参数是会存在歧义的。
Map提供了一个ofEntries的方法,用于更长的不变map集合

static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
    if (entries.length == 0) { // implicit null check of entries array
        @SuppressWarnings("unchecked")
        var map = (Map<K,V>) ImmutableCollections.EMPTY_MAP;
        return map;
    } else if (entries.length == 1) {
        // implicit null check of the array slot
        return new ImmutableCollections.Map1<>(entries[0].getKey(),
                entries[0].getValue());
    } else {
        Object[] kva = new Object[entries.length << 1];
        int a = 0;
        for (Entry<? extends K, ? extends V> entry : entries) {
            // implicit null checks of each array slot
            kva[a++] = entry.getKey();
            kva[a++] = entry.getValue();
        }
        return new ImmutableCollections.MapN<>(kva);
    }
}

同样的,改变不可变map集合元素会抛出异常。
因为MapN继承自AbstractImmutableMap

abstract static class AbstractImmutableMap<K,V> extends AbstractMap<K,V> implements Serializable {
    @Override public void clear() { throw uoe(); }
    @Override public V compute(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
    @Override public V computeIfAbsent(K key, Function<? super K,? extends V> mf) { throw uoe(); }
    @Override public V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> rf) { throw uoe(); }
    @Override public V merge(K key, V value, BiFunction<? super V,? super V,? extends V> rf) { throw uoe(); }
    @Override public V put(K key, V value) { throw uoe(); }
    @Override public void putAll(Map<? extends K,? extends V> m) { throw uoe(); }
    @Override public V putIfAbsent(K key, V value) { throw uoe(); }
    @Override public V remove(Object key) { throw uoe(); }
    @Override public boolean remove(Object key, Object value) { throw uoe(); }
    @Override public V replace(K key, V value) { throw uoe(); }
    @Override public boolean replace(K key, V oldValue, V newValue) { throw uoe(); }
    @Override public void replaceAll(BiFunction<? super K,? super V,? extends V> f) { throw uoe(); }
    ...
}

使用方式:

Map<Integer, Integer> map = Map.of(1,2,3,4,5,6);
for (Integer i:map.keySet()) {
    System.out.println("key:" + i + ", value:" + map.get(i));
}
Map<Integer, Integer> map1 = Map.ofEntries(Map.entry(1,3));

4 总结

  1. 可以通过List、Set、Map接口的of方法构造不可变集合
  2. 不可变集合通过父类抽象类实现能够变动的接口,并在其中直接抛异常实现
  3. 不可变集合由于改动会抛异常,所以使用是安全的

你可能感兴趣的:(#,Java基础知识,java,windows,开发语言)