Guava记录 - 新集合类型RangeSet

一、简介

Guava - RangeSet是Guava中新出的集合类型。官网对其描述如下:

A RangeSet describes a set of disconnected, nonempty ranges. When adding a range to a mutable RangeSet, any connected ranges are merged together, and empty ranges are ignored.

大致意思为:

RangeSet描述了一组断开连接、非空的Ranges.当将range添加到一个可变的RangeSet时,所有的ranges将会合并到一起,空的ranges将会被忽略.

可以理解为:

新出的RangeSet类型是一个操作数学应用中的区间的新集合类型.它可以存储多个非空区间.通过应用中的左闭右开左开右闭等规则,会将相连的区间merge成一个区间(多区间的并集操作),并且提供了这些区间的视图操作方法等.让我们遇到区间需求的业务代码时,开发起来更优雅、更方便.

值得注意的是:

GWT下不支持RangeSet,在JDK 1.5 backport中也不支持;RangeSet要求完全使用JDK 1.6中的NavigableMap特性,因此JDK的版本不得低于1.6.

二、常用方法

1、修改类型
a、增加元素方法 - add()

如同普通的Set集合一样,RangeSet也提供了添加集合元素的方法.不过此时元素的的类型被要求为区间元素Range.区间的类型可以分为开区间闭区间左开右闭左闭右开..

实验代码:

        System.out.println("====================测试 new collection Types====================");
        RangeSet<Integer> rangeSet = TreeRangeSet.create();
        // 添加闭区间{[1, 10]}
        rangeSet.add(Range.closed(1, 10));
        System.out.println("============测试输出添加一个闭区间之后的rangeSet===============" + rangeSet);
        // disconnected range: {[1, 10], [11, 15)}
        rangeSet.add(Range.closedOpen(12, 15));
        System.out.println("============测试输出添加一个左闭右开区间之后的rangeSet===============" + rangeSet);
        // connected range; {[1, 10], [11, 20)}
        rangeSet.add(Range.closedOpen(15, 20));
        System.out.println("============测试输出一个相邻左闭右开区间之后的rangeSet===============" + rangeSet);
        // empty range; {[1, 10], [11, 20)}
        rangeSet.add(Range.openClosed(25, 30));
        

实验结果:

====================测试 new collection Types====================
============测试输出添加一个闭区间之后的rangeSet===============[[1..10]]
============测试输出添加一个左闭右开区间之后的rangeSet===============[[1..10], [12..15)]
============测试输出一个相邻左闭右开区间之后的rangeSet===============[[1..10], [12..20)]
============测试输出添加一个左开右闭之后的rangeSet===============[[1..10], [12..20), (25..30]]
b、移除元素方法 - remove()removeAll()

如同普通的Set集合一样,RangeSet也提供了移除集合元素的方法.
remove(): 参数的的类型被要求为Range - 区间的类型可以分为开区间闭区间左开右闭左闭右开..
removeAll(): 参数的类型被要求为RangeSet.

实验代码:

        // splits [1, 10]; {[1, 5], [10, 10], [11, 20)}
        rangeSet.remove(Range.open(5, 10));
        System.out.println("============测试输出remove闭区间之后的rangeSet===============" + rangeSet);
        rangeSet.remove(Range.closed(11, 12));
        System.out.println("============测试输出remove闭区间之后的rangeSet===============" + rangeSet);
        rangeSet.remove(Range.openClosed(13, 14));
        System.out.println("============测试输出remove左开右闭之后的rangeSet===============" + rangeSet);
        rangeSet.remove(Range.closedOpen(15, 16));
        System.out.println("============测试输出remove左闭右开之后的rangeSet===============" + rangeSet);
        rangeSet.removeAll(TreeRangeSet.create());
        System.out.println("============测试输出removeAll之后的rangeSet===============" + rangeSet);

实验结果:

============测试输出remove闭区间之后的rangeSet===============[[1..5], [10..10], [12..20), (25..30]]
============测试输出remove闭区间之后的rangeSet===============[[1..5], [10..10], (12..20), (25..30]]
============测试输出remove左开右闭之后的rangeSet===============[[1..5], [10..10], (12..13], (14..20), (25..30]]
============测试输出remove左闭右开之后的rangeSet===============[[1..5], [10..10], (12..13], (14..15), [16..20), (25..30]]
============测试输出removeAll之后的rangeSet===============[[1..5], [10..10], (12..13], (14..15), [16..20), (25..30]]

2、视图类型

RangeSet还实现支持非常广泛的视图

a、获取补集 - complement()

如同数学应用中的区间操作一样,RangeSet支持获取当前集合区间中的补集.
complement()就是这样的一个方法。

值得一提的是: 补集也是一个RangeSet.

实验代码:

        System.out.println("==========测试complement方法==============" + rangeSet.complement());

实验结果:

==========测试complement方法==============[(-∞..1), (5..10), (10..12], (13..14], [15..16), [20..25], (30..+∞)]
b、获取与指定区间的交集 - subRangeSet(Range<C>)

RangeSet除了支持取补集的方法外,同样也支持取交集的方法.subRangeSet(Range<C>)就是这样一个获取交集的方法,不过它获取交集不是两个RangeSet之间取交集,而是指定一个Range与原有Range集合取交集.这泛化了传统排序集合的headSetsubSettailSet视图。

实验代码:

        //测试与指定range取交集
        System.out.println("==========测试subRangeSet方法==============" + rangeSet.subRangeSet(Range.closed(3, 5)));

实验结果:

==========测试subRangeSet方法==============[[3..5]]
c、迭代操作 - asRanges()

之前提到了RangeSet是多个Range区间的集合,那么当我们想对集合中的某些目标区间进行操作时,我们就需要用到asRanges()方法.

asRanges()RangeSet视为一个Range集合,让我们可以对其进行迭代操作。

实验代码:

System.out.println("==========测试元素所处范围asRanges()方法==============" + rangeSet.asRanges());
        rangeSet.asRanges()
                .stream()
                .filter(Objects::nonNull)
                .forEach(integerRange -> {
                    System.out.println("==========当前Range信息=========" + integerRange);
                    Integer testElement = NumberUtils.INTEGER_ONE;
                    System.out.println("===============当前Range是否包含测试节点========" + integerRange.contains(testElement));
                    System.out.println("===============当前Range最小节点============" + integerRange.lowerEndpoint());
                    System.out.println("===============当前Range最大节点============" + integerRange.upperEndpoint());
                    System.out.println("===============当前Range是否有上限============" + integerRange.hasUpperBound());
                    System.out.println("===============当前Range是否有下限============" + integerRange.hasLowerBound());
                });

实验结果:

==========测试元素所处范围asRanges()方法==============[[1..10], [12..20), (25..30]]

==========当前Range信息=========[1..10]
===============当前Range是否包含测试节点========true
===============当前Range最小节点============1
===============当前Range最大节点============10
===============当前Range是否有上限============true
===============当前Range是否有下限============true

==========当前Range信息=========[12..20)
===============当前Range是否包含测试节点========false
===============当前Range最小节点============12
===============当前Range最大节点============20
===============当前Range是否有上限============true
===============当前Range是否有下限============true

==========当前Range信息=========(25..30]
===============当前Range是否包含测试节点========false
===============当前Range最小节点============25
===============当前Range最大节点============30
===============当前Range是否有上限============true
===============当前Range是否有下限============true

可以看出通过迭代操作,输出了每一个区间所在的最小节点最大节点是否有上限是否有下限以及是否包含指定元素等信息.

3、查询类型

RangeSet除了支持我们上述的关于取交集补集迭代操作之外,还直接支持几个查询操作.

a、查询元素是否存在 - contains()

RangeSet中最常用的一个方法,查询整个RangeSet所有区间中是否包含了指定元素.如果包含返回true否则返回false.

实验代码:

        System.out.println("==========测试contains()方法==============" + rangeSet.contains(NumberUtils.INTEGER_ONE));
        System.out.println("==========测试contains()方法==============" + rangeSet.contains(NumberUtils.INTEGER_ZERO));

实验结果:

==========测试contains()方法==============true
==========测试contains()方法==============false
b、查询元素存在哪个区间 - rangeContaining()

有时我们的需求可能并不仅仅只是想知道元素是否存在,更想知道元素所在于哪个区间,这时就需要用到我们的rangeContaining()方法.rangeContaining()方法的作用就在于查询指定元素所在的Range,如果有符合的Range就将其返回,否则返回NULL.

实验代码:

        System.out.println("==========测试rangeContaining()方法==============" + rangeSet.rangeContaining(NumberUtils.INTEGER_ONE));
        System.out.println("==========测试rangeContaining()方法==============" + rangeSet.rangeContaining(NumberUtils.INTEGER_ZERO));

实验结果:

==========测试rangeContaining()方法==============[1..10]
==========测试rangeContaining()方法==============null
c、查询是否包含指定区间 - encloses()enclosesAll()

RangeSet也提供了验证指定区间是否包含在RangeSet集合中的方法.
encloses(): 查询RangeSet是否包含指定区间.
enclosesAll(): 查询RangeSet是否包含指定的RangeSet.

实验代码:

        //测试是否包含指定区间
        System.out.println("==========测试encloses()方法==============" + rangeSet.encloses(Range.closed(1, 5)));
        System.out.println("==========测试encloses()方法==============" + rangeSet.encloses(Range.closed(-1, 5)));
        
        //测试是否包含指定区间集
        System.out.println("==========测试enclosesAll()方法==============" + rangeSet.enclosesAll(TreeRangeSet.create()));
        System.out.println("==========测试enclosesAll()方法==============" + rangeSet.enclosesAll(rangeSet.complement()));

实验结果:

==========测试encloses()方法==============true
==========测试encloses()方法==============false

==========测试enclosesAll()方法==============true
==========测试enclosesAll()方法==============false
d、获取整个RangeSet边界的区间 - span()方法

除了上述的方法外,RangeSet还提供了一个比较有用的方法 - span(),它可以获取整个并集最大元素、最小元素作为边界的区间.

实验代码:

        System.out.println("==========测试all方法==============" + rangeSet);
        System.out.println("==========测试span()方法==============" + rangeSet.span());
        System.out.println("==========测试span()方法==============" + rangeSet.span().contains(11));

实验结果:

==========测试all方法==============[[1..10], [12..20), (25..30]]
==========测试span()方法==============[1..30]
==========测试span()方法==============true

......未完待续

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353