最近在做规则校验,未避免不正常的数据出现,需要做范围校验。找了部分工具类,工具类能够支撑的较少,最后选择了Guava的Range类实现范围校验。可以计算区间是否连接,取交集,并集等操作。
Maven repository:https://mvnrepository.com/artifact/com.google.guava/guava
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>31.0.1-jreversion>
dependency>
注意:
guava
版本号小于30.0-jre
的存在安全漏洞
public static void main(String[] args) {
// [1,2] 全部闭区间
Range<Integer> one = Range.closed(1, 2);
System.out.println(one);
// [2,5) 左闭右开
Range<Integer> two = Range.closedOpen(2, 5);
System.out.println(two);
// (2,5) 全部开区间
Range<Integer> three = Range.open(2, 5);
System.out.println(three);
// (2,5] 左开右闭
Range<Integer> four = Range.closedOpen(2, 5);
System.out.println(four);
// (6,10] 左开右闭
Range<Integer> five = Range.closedOpen(6, 10);
System.out.println(five);
// (6..+∞)
Range<Integer> six = Range.greaterThan(6);
System.out.println(six);
// 区间是否连接
// 如果存在一个 (可能为空) 范围,则返回true ,该范围由该范围和其他包围。
// 例如,
// [2,4) 和 [5,7) 未连接
// [2,4) 和 [3,5) 是连接的,因为两者都包围了 [3,4)
// [2,4) 和 [4,6) 是连接的,因为两者都包围了空范围 [4,4)
// 请注意,当且仅当此方法返回true时,此范围和其他具有定义明确的union和intersection (作为单个,可能为空的范围)。
// 连通性关系既是自反的又是对称的,但由于它不是传递的,因此不会形成等价关系。
// 请注意,即使 “在它们之间” 没有元素,某些离散范围也不被认为是连通的。例如, [3,5] 不被视为与 [6,10] 相连。在这些情况下,可能希望在测试连接性之前用规范 (离散域) 对两个输入范围进行预处理。
boolean connected = one.isConnected(two);
System.out.println(one + "和" + two + "is connected:" + connected);
//返回包含该范围和其他的最小范围。例如, [1 .. 3] 和 (5..7) 的跨度为 [1..7)。
// 如果输入范围为connected ,则返回的范围也可以称为union。如果不是,请注意,span可能包含两个输入范围中未包含的值。
// 和交集一样,这个操作是可交换的,结合的,幂等的。与它不同,它总是为任何两个输入范围定义良好。
// 并集
Range<Integer> span = one.span(two);
System.out.println(one + "和" + two + "并集为:" + span);
//
Range<Integer> span2 = four.span(five);
System.out.println(four + "和" + five + "并集为:" + span2);
// 交集
//如果存在该范围,则返回该范围和connectedRange包围的最大范围。
// 例如, [1 .. 5] 和 (3..7) 的交集为 (3 .. 5] 。结果范围可能为空; 例如, [1..5) 与 [5..7) 相交产生 [5..5) 的空范围。
// 当且仅当两个范围连通时,交点才存在。
// 交集运算为可交换、结合和幂等,其单位元为all )。
// 投掷:
// IllegalArgumentException -如果isConnected(connectedRange) 为false
Range<Integer> integerRange = one.intersection(two);
System.out.println(one + "和" + two + "交集为:" + integerRange);
}
[1..2]
[2..5)
(2..5)
[2..5)
[6..10)
(6..+∞)
[1..2]和[2..5)is connected:true
[1..2]和[2..5)并集为:[1..5)
[2..5)和[6..10)并集为:[2..10)
[1..2]和[2..5)交集为:[2..2]
注意:
Range
类的方法包含了空范围的场景,例如:[3,3)
,在具体使用时,需要根据实际场景做进一步处理
上边通过简单的例子说明了Range
类的简单使用,接下来对Range
类详细方法做一个说明。
Guava
中的Range
要求上端点不能小于下端点,否者会抛出IllegalArgumentException
异常,上下端点可能是相等的(可能存在空区间的场景)。
Range
的区间是闭区间或半开半闭区间(至少有一个端点是包含在区间中),比如:
[5..5]
:单元素区间[5..5); (5..5]
:空区间,但它们是有效的(5..5)
:无效区间Guava
提供了BoundType
枚举类来指定区间边界的类型,包含 CLOSED 和 OPEN 两个值。
range(C, BoundType, C, BoundType)
((a..+∞) 或[a..+∞)) downTo(C, BoundType)
((-∞..b) 或(-∞..b]) upTo(C, BoundType)
System.out.println(Range.open(1, 2)); //(1..2)
System.out.println(Range.closed(1, 2)); // [1..2]
System.out.println(Range.closedOpen(1, 2)); // [1..2)
System.out.println(Range.openClosed(1, 2)); // (1..2]
System.out.println(Range.greaterThan(1)); // (1..+∞)
System.out.println(Range.atLeast(1)); // [1..+∞)
System.out.println(Range.lessThan(2)); // (-∞..2)
System.out.println(Range.atMost(2)); // (-∞..2]
System.out.println(Range.range(1, BoundType.CLOSED, 2, BoundType.OPEN)); // [1..2)
System.out.println(Range.downTo(5, BoundType.OPEN)); // (5..+∞)
System.out.println(Range.upTo(5, BoundType.CLOSED)); // (-∞..5]
判断区间是否包含某个值
boolean contains = Range.closed(2, 10).contains(3);
System.out.println(contains);//true
Range
类提供了以下方法来查看区间的端点:
hasLowerBound()
和hasUpperBound()
:判断区间是否有特定边界,或是无限的;lowerBoundType()
和upperBoundType()
:返回区间边界类型,CLOSED
或 OPEN
;如果区间没有对应的边界,抛出 IllegalStateException
lowerEndpoint()
和upperEndpoint()
:返回区间的端点值;如果区间没有对应的边界,抛出 IllegalStateException
;isEmpty()
:判断是否为空区间。Range.closedOpen(4, 4).isEmpty(); // returns true
Range.openClosed(4, 4).isEmpty(); // returns true
Range.closed(4, 4).isEmpty(); // returns false
Range.open(4, 4).isEmpty(); // Range.open throws IllegalArgumentException
Range.closed(3, 10).lowerEndpoint(); // returns 3
Range.open(3, 10).lowerEndpoint(); // returns 3
Range.closed(3, 10).lowerBoundType(); // returns CLOSED
Range.open(3, 10).upperBoundType(); // returns OPEN
System.out.println(Range.open(3, 10).hasLowerBound());//(3,10) true
System.out.println(Range.atMost( 10).hasLowerBound()); //(-∞,10) false
System.out.println(Range.atLeast( 10).hasLowerBound()); //(10,+∞) true
System.out.println(Range.atLeast( 10).hasUpperBound()); //(10,+∞) false
区间之间的最基本关系就是包含encloses(Range)
:
如果内区间的边界没有超出外区间的边界,则外区间包含内区间。包含判断的结果完全取决于区间端点的比较
// [3..6] 包含[4..5] ;
System.out.println(Range.closed(3,6).encloses(Range.closed(4,5)));//true
// (3..6) 包含(3..6) ;
System.out.println(Range.open(3,6).encloses(Range.open(3,6)));//true
// [3..6] 包含[4..4),虽然后者是空区间;
System.out.println(Range.closed(3,6).encloses(Range.closedOpen(4,4)));//true
// (3..6]不包含[3..6] ;
System.out.println(Range.openClosed(3,6).encloses(Range.closed(3,6)));//false
// [4..5]不包含(3..6)
System.out.println(Range.closed(4,5).encloses(Range.open(3,6)));//false
Range.isConnected(Range)
判断区间是否是相连的。isConnected
测试是否有区间同时包含于这两个区间,这等同于数学上的定义”两个区间的并集是连续集合的形式”(空区间的特殊情况除外)。
System.out.println(Range.closed(3,5).isConnected(Range.open(5,10)));//true
System.out.println(Range.closed(0,9).isConnected(Range.closed(3,4)));//true
System.out.println(Range.closed(0,5).isConnected(Range.closed(3,9)));//true
System.out.println(Range.open(3,5).isConnected(Range.open(5,10)));//false
System.out.println(Range.closed(1,5).isConnected(Range.closed(6,10)));//false
// 注意空区间形式
System.out.println(Range.openClosed(5,5).isConnected(Range.closedOpen(5,5)));//false
Range.intersection(Range)
返回两个区间的交集:既包含于第一个区间,又包含于另一个区间的最大区间。
**当且仅当两个区间是相连的,它们才有交集。**如果两个区间没有交集,该方法将抛出 IllegalArgumentException
。使用前可以先校验是否是相连的。
System.out.println(Range.closed(3,5).intersection(Range.closed(4,6)));//[4..5]
Range.span(Range)
返回”同时包括两个区间的最小区间“,如果两个区间相连,那就是它们的并集。
System.out.println(Range.closed(3,5).span(Range.closed(7,9)));//[3..9]
System.out.println(Range.closed(3,5).span(Range.closed(4,8)));//[3..8]
- Java工具库Guava的区间(范围Range)的构建、区间运算、查询运算、关系运算(包含、相连、交集、并集)的使用示例 - 霸道流氓 - 博客园