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.Builderide

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)。ui

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能够做为常量来使用,相信你们在本身的项目中确定会有这样的需求。this

  1. 初始化集合做为筛选用,黑名单功能
  2. 防止返回的集合引用,被他人误用,修改原集合,致使bug出现
相关文章
相关标签/搜索