1. 程式人生 > >Guava TreeMultiSet實現原理分析(2)

Guava TreeMultiSet實現原理分析(2)

5 count,size

AvlNode為資料統計提供了多個便利引數,不需要遍歷所有的子節點就可以獲得相關的個數資訊。 AvlNode的統計屬性: elemCount:統計key相同的元素個數。 distinctElements:統計子樹中所有節點的個數,即key不同的元素個數。 totalCount:子樹中所有元素的個數。 count:count的實際操作,是在AvlNode中完成。TreeMultiset.count()呼叫root的count。root是AvlNode的一個例項。root會遞迴比對,找到元素的節點,返回節點的elemCount屬性值。 size:size的需要找到邊界的上下限,然後用root的totalCount減去上下限之外的個數。邏輯主要封裝在aggregateForEntries方法中:

  private long aggregateForEntries(Aggregate aggr) {
    AvlNode<E> root = rootReference.get();
    long total = aggr.treeAggregate(root);
    if (range.hasLowerBound()) {
      total -= aggregateBelowRange(aggr, root);
    }
    if (range.hasUpperBound()) {
      total -= aggregateAboveRange(aggr, root);
    }
    return total;
  }

aggregateBelowRange,aggregateAboveRange方法是通過遞迴比較,找到邊界。

6 add

有了前面的鋪墊,我們可以理解了TreeMultiset的基本組建方式,能更好的理解add操作。 add對外暴露了兩個介面:

public boolean add(@Nullable E element);
public int add(@Nullable E element, int occurrences);

第一個介面新增element到樹中,返回是否成功的標識。其內部呼叫第二個介面。 第二個介面通過引數occurrences來定義操作。如果occurrences> 0 ,則代表element插入的個數;如果occurrences == 0,則代表統計element的元素個數。count的方法在前面已經介紹。在occurrences>0的時候,先判斷根節點是否存在。如果根節點不存在,則用element建立根節點並返回;如果根節點已經存在,則真正執行插入操作。TreeMultiset並沒有定義具體的插入邏輯,而是放在AvlNode中進行。 在AvlNode.add中,按照樹操作的邏輯,分成三個邏輯:element與當前節點比較後,放在左節點、右節點以及匹配到當前節點。 左節點和右節點的操作類似,放在一起介紹: 如果左節點是null,則直接用element建立左節點。 如果左節點不是null,則左節點遞迴呼叫add方法。遞迴的結果是AvlNode物件,需要將此物件賦值給當前的左節點。這麼做主要是因為左節點可能引發平衡操作。如果引發平衡操作,那麼之前的左節點將會發生變化。 根據返回的資訊,判斷是否改變了樹的高度。如果左子樹的高度發生了變化,則在當前節點呼叫平衡演算法。AvlNode就是採用這種遞迴判斷的方式,保證整棵樹的平衡。 匹配到當前節點,則直接elemCount+occurrences,totalCount+occurrences

從這裡可以看出,TreeMultiset並沒有儲存所有的資料,而是將相等的資料進行壓縮。如果兩個Element的比較結果為相等,則只會儲存第一個Element。當第二個Element加入時,在計數器上加一。

7 remove

remove和add的操作流程類似。 先進行遞迴匹配,找到相等的節點。減去節點的elemCount。如果減去的數目大於elemCount,則刪除此節點。如果發生高度變化,則進行平衡操作。