1.Immutable Collections
不可变集合,特点:
-
被不受信任的库调用是安全的
-
线程安全
-
不需要考虑值的变化从而节省时间和空间
-
可以用作常量
-
不接收null值
创建一个不可变集合的简单例子如下:
public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of(
"red",
"orange",
"yellow",
"green",
"blue",
"purple");
class Foo {
final ImmutableSet<Bar> bars;
Foo(Set<Bar> bars) {
this.bars = ImmutableSet.copyOf(bars); // defensive copy!
}
}
值得注意的是,copyOf是浅拷贝。
Map<Integer, StringBuilder> map = new HashMap<>();
map.put(1, new StringBuilder("1"));
map.put(2, new StringBuilder("2"));
Map<Integer, StringBuilder> unmodifiableMap = Collections.unmodifiableMap(map);
unmodifiableMap.get(2).append("unmodifiable");
System.out.println(unmodifiableMap.get(2)); // 2unmodifiable
Map<Integer, StringBuilder> immutableMap = ImmutableMap.copyOf(map);
immutableMap.get(2).append("immutable");
System.out.println(immutableMap.get(2)); //2unmodifiableimmutable
System.out.println(map.get(2)); // 2unmodifiableimmutable
System.out.println(map.get(2) == immutableMap.get(2)); // true
System.out.println(map.get(2) == unmodifiableMap.get(2)); // true
map.put(3, new StringBuilder("3"));
System.out.println(unmodifiableMap.get(3)); // 3
System.out.println(immutableMap.get(3)); // null
上述例子中提到了Collections.unmodifiableXXX,两者的差别:
- 只有外部没有拥有对象引用的时候Collections.unmodifiableXXX才是真正不可变的
-
笨重而冗长
- 低效,返回的数据结构仍然具有可变集合的所有开销
关于低效的解释,可以简单看一下unmodifiableMap的实现。
public class Collections {
...
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
return new UnmodifiableMap<>(m);
}
private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
private static final long serialVersionUID = -1034234728574286014L;
private final Map<? extends K, ? extends V> m;
UnmodifiableMap(Map<? extends K, ? extends V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
}
...
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public V remove(Object key) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
...
}
}
可以看到,UnmodifiableMap仅仅对Map接口中修改值的实现重写为不可用,对源引用并没有再做拷贝,所以对源引用来说,数据结构没有发生变化,本质上仍是可变的,要承担可变的开销(并发修改检查、哈希表中的额外空间等)。
这在一些场景下是适用的,但是guava认为这种不可变不够彻底,可以简单看下ImmutableList的实现,做一个对比。
@GwtCompatible(serializable = true, emulated = true)
@SuppressWarnings("serial")
public abstract class ImmutableList<E> extends ImmutableCollection<E>
implements List<E>, RandomAccess {
...
public static <E> ImmutableList<E> copyOf(Collection<? extends E> elements) {
if (elements instanceof ImmutableCollection) {
@SuppressWarnings("unchecked") // all supported methods are covariant
ImmutableList<E> list = ((ImmutableCollection<E>) elements).asList();
return list.isPartialView() ? ImmutableList.<E>asImmutableList(list.toArray()) : list;
}
return construct(elements.toArray());
}
private static <E> ImmutableList<E> construct(Object... elements) {
return asImmutableList(checkElementsNotNull(elements));
}
static <E> ImmutableList<E> asImmutableList(Object[] elements) {
return asImmutableList(elements, elements.length);
}
static <E> ImmutableList<E> asImmutableList(Object[] elements, int length) {
switch (length) {
case 0:
return of();
case 1:
return of((E) elements[0]);
default:
if (length < elements.length) {
elements = Arrays.copyOf(elements, length);
}
return new RegularImmutableList<E>(elements);
}
}
...
}
@GwtCompatible(serializable = true, emulated = true)
@SuppressWarnings("serial")
class RegularImmutableList<E> extends ImmutableList<E> {
...
static final ImmutableList<Object> EMPTY = new RegularImmutableList<>(new Object[0]);
@VisibleForTesting final transient Object[] array;
RegularImmutableList(Object[] array) {
this.array = array;
}
...
}
如果对象本身就是不可变集合,那么直接返回。
如果对象是可变集合,先检查集合元素是否不为null,然后再做一层拷贝,将拷贝结果返回。
尽管如此,guava的不可变集合并不是真正意义上的深拷贝,集合中的元素如果是引用,那么元素依然是可变的。
剩余问题:
https://www.jianshu.com/p/bf2623f18d6a
https://zhangzw.com/posts/20190718.html
https://demonyangyue.github.io/
@LazyInit
@VisibleForTesting
@GwtCompatible