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,则删除此节点。如果发生高度变化,则进行平衡操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值