您的位置:首页 > 其它

Guava中TreeRangeMap结构简析

2015-08-03 17:08 337 查看
TreeRangeMap之所以能实现按照Range进行排序,这要归功于内部NavigableMap类型的存储变量entriesByLowerBound。NavigableMap是一个接口,继承自SortedMap接口,所以TreeRangeMap具有排序功能。这些接口的使用可以查看此处总结

public final class TreeRangeMap<K extends Comparable, V> implements RangeMap<K, V> {
    private final NavigableMap<Cut<K>, TreeRangeMap.RangeMapEntry<K, V>> entriesByLowerBound = Maps.newTreeMap();
...
}

public interface NavigableMap<K,V> extends SortedMap<K,V> {
...
}

public interface SortedMap<K,V> extends Map<K,V> {
...
}


从entriesByLowerBound的定义可以体会到TreeRangeMap的巧妙。这个结构以Range中lowerBound的Cut作为键,以整个RangeMapEntry作为值,很天然的实现排序。

再来看看RangeMapEntry类,RangeMapEntry继承自抽象类AbstractMapEntry,而AbstractMapEntry继承自Entry接口,浑然一体的结构。

//定义在TreeRangeMap中,
private static final class RangeMapEntry<K extends Comparable, V> extends AbstractMapEntry<Range<K>, V> {
...
}

abstract class AbstractMapEntry<K, V> implements Entry<K, V> {
...
}
/
/定义在Map.java中,最基础的记录
interface Entry<K,V> {
...
}


AbstractMapEntry用来过渡,内部并没有数据实体,实现setValue,equals,hashCode,toString方法。

将Entry设计成Map的内部接口非常合理,因为Entry针对Map具体实现时候的一条记录。同时Map是一个接口,Entry也是一个接口,定义最基础结构时内部接口的写法值得学习。

有这些结构的支撑,TreeRangeMap能够实现切割插入,以便内部Range键不重叠。先来看看put方法

public void put(Range<K> range, V value) {
        if(!range.isEmpty()) {
            Preconditions.checkNotNull(value);
            this.remove(range);      //先截断删除
            this.entriesByLowerBound.put(range.lowerBound, new TreeRangeMap.RangeMapEntry(range, value));
            //然后lowerBound为键,RangeMapEntry为值,插入entriesByLowerBound中
        }
    }


插入的时候之所以不会重叠,是因为插入前会截断删除。再来看看remove操作:

remove操作分两个部分,第一部分是截断rangeToRemove低点所覆盖已有Range,第二部分是截断rangeToRemove高点所覆盖到已有Range,两个不分差不多,解释其中一个。

在所有if都成功的情况下,先取出严格小于rangeToRemove低点的最大低点对应的Entry(mapEntryBelowToTruncate)。这是利用NavigableMap对Range中低点存储有序进行。然后取出这个Entry的value值(mapEntryAboveToTruncate),对应的是个RangeMapEntry,mapEntryAboveToTruncate里面包含作为Key(这里的Key是RangeMap的Key)的Range(取名r)。

如果r包含rangeToRemove的低点,r有一部分跟rangeToRemove重叠,需要被切割;如果不包含证明没有重叠部分,这一部分通过了。

在包含的前提下,又分两种情况。

如果r包含rangeToRemove高点,那么r需要被rangeToRemove从中间拦腰截断。

如果r不包含rangeToRemove高点,那么r只是靠近低点的部分和rangeToRemove重叠,需要截断这个部分。

截断低点部分的源码如下:

Entry mapEntryBelowToTruncate = this.entriesByLowerBound.lowerEntry(rangeToRemove.lowerBound);
   if(mapEntryBelowToTruncate != null) {
       TreeRangeMap.RangeMapEntry mapEntryAboveToTruncate = (TreeRangeMap.RangeMapEntry)mapEntryBelowToTruncate.getValue();
       if(mapEntryAboveToTruncate.getUpperBound().compareTo(rangeToRemove.lowerBound) > 0) {
           if(mapEntryAboveToTruncate.getUpperBound().compareTo(rangeToRemove.upperBound) > 0) {
               this.putRangeMapEntry(rangeToRemove.upperBound, mapEntryAboveToTruncate.getUpperBound(), ((TreeRangeMap.RangeMapEntry)mapEntryBelowToTruncate.getValue()).getValue());
           }
           this.putRangeMapEntry(mapEntryAboveToTruncate.getLowerBound(), rangeToRemove.lowerBound, ((TreeRangeMap.RangeMapEntry)mapEntryBelowToTruncate.getValue()).getValue());
       }
   }


截断高点的部分类似。

(完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: