一、简介
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
集合取交集
.这泛化了传统排序集合的headSet
、subSet
和tailSet
视图。
实验代码:
//测试与指定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
......未完待续