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 总结
- 可以通过List、Set、Map接口的of方法构造不可变集合
- 不可变集合通过父类抽象类实现能够变动的接口,并在其中直接抛异常实现
- 不可变集合由于改动会抛异常,所以使用是安全的