這一次,咱們來分析ImmutableSet,與ImmutableList大同小異,建議你們先看完上一篇Immutable Collections(2),在繼續往下看html
相同:算法
不一樣:數組
EmptyImmutableSet、SingletonImmutableSet不須要在作更多詳細的說明,他們的存在的意義在於,對單個元素和空集合的操做優化,RegularImmutableSet的實現比較巧妙,它內部維護了兩套數組,一套依照傳入元素順序,記錄元素。另外一套根據Guava自定義的hash算法產生通過hash後的集合的位置,Guava的hash算法效率高於HashSet內部提供的hash算法,並且元素保證傳入順序。ide
private static <E> ImmutableSet<E> construct(int n, Object... elements) { //依然是根據元素個數的不一樣,選擇不一樣的實現方式 switch (n) { case 0: return of(); case 1: @SuppressWarnings("unchecked") // safe; elements contains only E's E elem = (E) elements[0]; return of(elem); default: // continue below to handle the general case } int tableSize = chooseTableSize(n); Object[] table = new Object[tableSize]; int mask = tableSize - 1; int hashCode = 0; int uniques = 0; for (int i = 0; i < n; i++) { Object element = checkElementNotNull(elements[i], i); int hash = element.hashCode(); for (int j = Hashing.smear(hash); ; j++) { int index = j & mask; Object value = table[index]; if (value == null) { // Came to an empty slot. Put the element here. elements[uniques++] = element;//原有元素徹底不變的copy到elements集合中 table[index] = element;//table中存的是hash計算後的元素 hashCode += hash;//hash值 break; } else if (value.equals(element)) { break; } } } Arrays.fill(elements, uniques, n, null); if (uniques == 1) { // There is only one element or elements are all duplicates @SuppressWarnings("unchecked") // we are careful to only pass in E E element = (E) elements[0]; return new SingletonImmutableSet<E>(element, hashCode); //若是table的size和uniques差的不少,證實重複元素不少,須要從新計算table的size,避免空間浪費 } else if (tableSize != chooseTableSize(uniques)) { return construct(uniques, elements); } else { Object[] uniqueElements = (uniques < elements.length) ? ObjectArrays.arraysCopyOf(elements, uniques) : elements; return new RegularImmutableSet<E>(uniqueElements, hashCode, table, mask); } }
並且沒必要擔憂空間的浪費,其實兩套數組內部,只是維護元素的引用。若是傳入的元素集合存在大量的重複元素,ImmutableSet會copy後從新構造Set,以減小table數組(hash事後的)的空間浪費。詳見代碼。優化
那麼,在RegularImmutableSet中,與set相關的O(1)操做,就會轉爲對table的操做,而對set的遍歷等操做,會轉爲對elements數組的操做,代碼以下:this
RegularImmutableSet( Object[] elements, int hashCode, Object[] table, int mask) { this.elements = elements;//維護原有集合順序 this.table = table;//通過hash後的集合 this.mask = mask; this.hashCode = hashCode; } //contains操做,使用通過hash後的table @Override public boolean contains(Object target) { if (target == null) { return false; } for (int i = Hashing.smear(target.hashCode()); true; i++) { Object candidate = table[i & mask]; if (candidate == null) { return false; } if (candidate.equals(target)) { return true; } } } @Override public int size() { return elements.length;//size的實現確定使用elements,由於table在有少許重複元素的狀況下,稍大於elements }