Guava源码分析——Immutable Collections(1)

在Java中,conrrent包提供了很多线程安全的集合,但有的时候我们可以换一种方式对思考使用线程安全集合,Guava的Immutable提供了一系列不可变集合类型,不可变就使得集合成为了常量,常量必然线程安全。对于集合的不可变,除了Guava提供的Immutable Collections以外,还是有Collections.unmodifiableCollection(),而两者之间,还是有些区别的。从UML图中,可以看出,ImmutableCollection继承了AbstractCollection,从而也成为了Java标准的集合类。与标准集合相同,ImmutableCollection分别被继承为三种类型集合——List、Set、MultiSet。

与Collections.unmodifiableXXX有以下几种不同:

  1. 防御性copy,无论原集合怎样改变,经过ImmutableCollections.copyOf()方法返回的集合,无论原集合怎样变化,新集合都不会在变化,而Collections.unmodifiableCollection()与之相反。如下代码:
 @Test
 public void testCopy() {
   List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4);

    List<Integer> integers1 = ImmutableList.copyOf(numbers);
    List<Integer> integers2 = Collections.unmodifiableList(numbers);
   
 numbers.add(
0, -1);  Assert.assertEquals(1, integers1.get(0).intValue());//Pass   Assert.assertEquals(1, integers2.get(0).intValue());//Failure }

  2. Collections.unmodifiableCollection()修饰后的集合,仍然具有原集合的特性,而不是将集合转化为常量

@Test
public void testConstruct() { Set<Integer> numbers = Sets.newConcurrentHashSet(); Set<Integer> integers1 = Collections.unmodifiableSet(numbers);//生成不可变集合
  //虽然集合已经不可变,但仍然会在并发读取的时候发生CAS操作,不可变意味着线程安全,而原集合的CAS多此一举。

  for (Integer integer : integers1) {
    System.out.println(integer); 
  }
}

  3. 对于空间使用的节省,后面builder源码分析时候,会说到

ImmutableCollections类中将所有write方法都置为throw new UnsupportedOperationException()操作,这里需要说明的是抽象类ImmutableCollection.Builder

Guava提供了构造器方式的构造不可变集合,如下代码所示:

public void testBuilder() {
  ImmutableList.Builder<Integer> builder = new ImmutableList.Builder<Integer>();

  //绝对不要这样做,初始size为4,超过后,每次加入扩容新加入集合的个数,下面写法每次扩容1,后续每次都会导致扩容copy发生
  ImmutableList<Integer> in = builder.add(1).add(2).add(3).add(4)
      .add(5)//超过初始size,扩容copy发生,size变为5
      .add(6)//超过size,扩容copy发生
      .build();
  
  //只扩容一次
  ImmutableList<Integer> in2 = builder.add(1, 2, 3, 4, 5, 6).build();
}

ArrayBasedBuilder是ImmutableList.Builder和ImmutbleSet.Builder的底层实现,数组形式,每次扩容(初始化size为4)。

abstract static class ArrayBasedBuilder<E> extends ImmutableCollection.Builder<E> {
    Object[] contents;
    int size;
    
    ArrayBasedBuilder(int initialCapacity) {
      checkNonnegative(initialCapacity, "initialCapacity");
      this.contents = new Object[initialCapacity];
      this.size = 0;
    }
    
    /**
     * Expand the absolute capacity of the builder so it can accept at least
     * the specified number of elements without being resized.
     */
    private void ensureCapacity(int minCapacity) {
      if (contents.length < minCapacity) {
        this.contents = ObjectArrays.arraysCopyOf(
       //每次扩容到contents.size,如Collections.unmodifiableList(new ArrayList<T>)()每次扩容一半不同,不会存在空间浪费
this.contents, expandedCapacity(contents.length, minCapacity)); } } @Override public ArrayBasedBuilder<E> add(E element) { checkNotNull(element); ensureCapacity(size + 1); contents[size++] = element; return this; } }

Immutable可以作为常量来使用,相信大家在自己的项目中肯定会有这样的需求。

  1. 初始化集合作为筛选用,黑名单功能
  2. 防止返回的集合引用,被他人误用,修改原集合,导致bug出现
posted @ 2015-05-29 18:42  PoNa  阅读(1044)  评论(0编辑  收藏  举报