在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