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出現
相關文章
相關標籤/搜索