您的位置:首页 > 其它

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

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



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

防御性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可以作为常量来使用,相信大家在自己的项目中肯定会有这样的需求。

初始化集合作为筛选用,黑名单功能

防止返回的集合引用,被他人误用,修改原集合,导致bug出现
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: