1、为什么要使用不可变集合

优点:

  1. 当对象被不可信的库调用时,不可变形式是安全的;
  2. 不可变对象被多个线程调用时,不存在竞态条件问题;
  3. 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率;
  4. 不可变对象因为有固定不变,可以作为常量来安全使用。

创建对象的不可变拷贝是一项很好的防御性编程技巧。Guava 为所有 JDK 标准集合类型和 Guava 新集合类型都提供了简单易用的不可变版本。

JDK 也提供了 Collections.unmodifiableXXX 方法把集合包装为不可变形式,但Guava认为不够好:

  • 笨重而且累赘:不能舒适地用在所有想做防御性拷贝的场景;
  • 不安全:要保证没人通过原集合的引用进行修改,返回的集合才是事实上不可变的;
  • 低效:包装过的集合仍然保有可变集合的开销,比如并发修改的检查、散列表的额外空间,等等。

如果你没有修改某个集合的需求,或者希望某个集合保持不变时,把它防御性地拷贝到不可变集合是个很好的实践。

2、怎么使用不可变集合

2.1、创建不可变集合

// copyOf 方法,如 ImmutableSet.copyOf(set);
List<String> list = Arrays.asList("a", "b", "c");
ImmutableSet<String> strings = ImmutableSet.copyOf(list);

// of 方法,如 ImmutableSet.of(“a”, “b”, “c”)或 ImmutableMap.of(“a”, 1, “b”, 2);
ImmutableSet<String> of = ImmutableSet.of("a", "b", "c", "d", "e");

// Builder 工具
ImmutableSet<String> set = ImmutableSet.<String>builder()
	.add("a")
	.add("b")
	.add("c")
.build();

另外,对有序不可变集合来说,排序是在构造集合的时候完成的,如:

ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");

会在构造时就把元素排序为 a, b, c, d。

2.2、更智能的 copyOf

ImmutableXXX.copyOf 方法会尝试在安全的时候避免做拷贝——实际的实现细节不详,但通常来说是很智能的,比如:

ImmutableSet<String> foobar = ImmutableSet.of("foo", "bar", "baz");
	thingamajig(foobar);
	void thingamajig(Collection<String> collection) {
	ImmutableList<String> defensiveCopy = ImmutableList.copyOf(collection);
	...
}

在这段代码中,ImmutableList.copyOf(foobar)会智能地直接返回 foobar.asList(),它是一个 ImmutableSet的常量时间复杂度的 List 视图。

作为一种探索,ImmutableXXX.copyOf(ImmutableCollection)会试图对如下情况避免线性时间拷贝:

  • 在常量时间内使用底层数据结构是可能的——例如,ImmutableSet.copyOf(ImmutableList)就不能在常量时间内完成。
  • 不会造成内存泄露——例如,你有个很大的不可变集合 ImmutableList hugeList, ImmutableList.copyOf(hugeList.subList(0, 10))就会显式地拷贝,以免不必要地持有 hugeList 的引用。
  • 不改变语义——所以 ImmutableSet.copyOf(myImmutableSortedSet)会显式地拷贝,因为和基于比较器的 ImmutableSortedSet 相比,ImmutableSet对hashCode()和 equals 有不同语义。

在可能的情况下避免线性拷贝,可以最大限度地减少防御性编程风格所带来的性能开销。

2.3、asList视图

所有不可变集合都有一个 asList()方法提供 ImmutableList 视图,来帮助你用列表形式方便地读取集合元素。例如,你可以使用 sortedSet.asList().get(k)从 ImmutableSortedSet 中读取第 k 个最小元素。

asList()返回的 ImmutableList 通常是——并不总是——开销稳定的视图实现,而不是简单地把元素拷贝进 List。也就是说,asList 返回的列表视图通常比一般的列表平均性能更好,比如,在底层集合支持的情况下,它总是使用高效的 contains 方法。

2.4、关联可变集合和不可变集合

3、例子

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;

public class Guava {
	public static void main(String[] args) {
	//创建 3钟方式
        //1、of方法
        ImmutableList<String> immutableList = ImmutableList.of("1", "3", "2");
        System.out.println(immutableList);
        //2、copyOf方法
        ImmutableList<Object> immutableList1 = ImmutableList.copyOf(Lists.newArrayList(1, "abc", 3d));
        System.out.println(immutableList1);
        //3、builder构建
        ImmutableList<Integer> immutableList2 = ImmutableList.<Integer>builder().add(3).add(5).add(7).build();
        System.out.println(immutableList2);

        ImmutableSet<String> immutableSet = ImmutableSet.of("beibei", "jingjing", "huanhuan");
        System.out.println(immutableSet);
        //不可变集合都可以使用asList方法返回一个ImmutableList视图
        ImmutableList<String> immutableSetToList = immutableSet.asList();
        System.out.println(immutableSetToList);

        //会自动排序
        ImmutableSortedSet<String> immutableSortedSet = ImmutableSortedSet.of("1", "32", "3");
        System.out.println(immutableSortedSet);

        ImmutableMap<String, Integer> immutableMap = ImmutableMap.of("1", 11, "2", 22);
        System.out.println(immutableMap);
        System.out.println(immutableMap.asMultimap());
	}
}
输出结果:
[1, 3, 2]
[1, abc, 3.0]
[3, 5, 7]
[beibei, jingjing, huanhuan]
[beibei, jingjing, huanhuan]
[1, 3, 32]
{1=11, 2=22}
{1=[11], 2=[22]}

4、小结

Guava创建不可变集合的这些方法里,ImmutableList、ImmutableSet、ImmutableSortedSet都是继承了所有集合的父类Collection,创建出来的集合可以使用JDK相对应集合的方法,是可以直接赋给JDK相对应集合的。其中ImmutableMap是实现Map接口的。

ImmutableList<String> immutableList = ImmutableList.of("1", "3", "2");
List<String> list = new ArrayList<String>();
list = immutableList;
immutableList.get(0);
11-23 04:38