在Java中,conrrent包提供了不少线程安全的集合,但有的时候咱们能够换一种方式对思考使用线程安全集合,Guava的Immutable提供了一系列不可变集合类型,不可变就使得集合成为了常量,常量必然线程安全。对于集合的不可变,除了Guava提供的Immutable Collections之外,仍是有Collections.unmodifiableCollection(),而二者之间,仍是有些区别的。从UML图中,能够看出,ImmutableCollection继承了AbstractCollection,从而也成为了Java标准的集合类。与标准集合相同,ImmutableCollection分别被继承为三种类型集合——List、Set、MultiSet。数组
与Collections.unmodifiableXXX有如下几种不一样:安全
@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