Guava源碼分析——Immutable Collections(3)

這一次,咱們來分析ImmutableSet,與ImmutableList大同小異,建議你們先看完上一篇Immutable Collections(2),在繼續往下看html

相同:算法

  • ImmutableSet底層也採用數組實現
  • of()、copyOf()方法實現邏輯也相同
  • 元素也是按傳入順序排列的
  • 實現是根據元素個數,分爲EmptyImmutableSet、SingletonImmutableSet、RegularImmutableSet

不一樣:數組

  • construct()方法的實現再也不是簡單的copy,須要計算hash值,而且ImmutableSet內部維護兩套數組(後面會說到)

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);
    }
  }
View Code

並且沒必要擔憂空間的浪費,其實兩套數組內部,只是維護元素的引用。若是傳入的元素集合存在大量的重複元素,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
    }
相關文章
相關標籤/搜索