Java 范围比较的推荐姿势
Posted 明明如月学长
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 范围比较的推荐姿势相关的知识,希望对你有一定的参考价值。
一、背景
在平时工作开发过程中,很容易遇到判断某个值是否在某个范围的场景。
如需要校验某个日期是否在某个范围;需要校验某个版本号是否在某个区间;需要校验某个时间点是否在某个时间段内;判断某个人是否属于某个年龄段;判断某个用户的积分是否属于某个等级的区间等。
前一阵子,技术群里有哥们就提了类似的一个问题:
判断当前时间是否在周期的时间段里面有什么好的办法吗 比如 当前时间是2021-10-1 5:00:00 ,设置的时间段为 2021-9-30 1:00:00 -2021-9-30 18:00:00
周期为1天 。 那么每天的5-18点都在周期的时间段里面。 [合十]
有图有真相
群里也有不少同学表达自己的建议
还有
那么,有没有比较优雅的判断方式呢?
二、建议
如果大家花点心思就可以对这些问题进行抽象,即所谓的范围就是数学里面的区间概念,是否在某个范围,即是否在该区间。
因此,我们可以定义一个区间,然后封装一个函数,传入某个值(区间上的某个点),返回是否在这个区间范围。
Guava
中提供了 com.google.common.collect.Range
类,就是为了解决这个问题。
同时还提供了一系列相关类如 RangeSet
、ImmutableRangeSet
,可以帮助我们轻松实现区间合并,区间判断是否有重叠,实现区间的不可变特性等,非常强大,超级推荐。
maven 地址
https://mvnrepository.com/artifact/com.google.guava/guava
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
<!-- or, for android: -->
<version>31.0.1-android</version>
</dependency>
gradle 依赖
dependencies {
// Pick one:
// 1. Use Guava in your implementation only:
implementation("com.google.guava:guava:31.0.1-jre")
// 2. Use Guava types in your public API:
api("com.google.guava:guava:31.0.1-jre")
// 3. Android - Use Guava in your implementation only:
implementation("com.google.guava:guava:31.0.1-android")
// 4. Android - Use Guava types in your public API:
api("com.google.guava:guava:31.0.1-android")
}
使用非常容易,只要需要比较的类型实现了 Comparable
接口,就直接可构造区间,然后拿值判断是否在这个区间中。
源码地址:
https://github.com/google/guava
主要函数,大家可去源码里自行了解
使用范例:
public void test_Closed(){
// 1点20分
LocalTime start = LocalTime.of(1,20);
// 8点12分
LocalTime end = LocalTime.of(8,12);
// 构造闭区间
Range<LocalTime> localTimeRange = Range.closed(start,end);
// 测试
LocalTime time1 = LocalTime.of(6,4);
Assert.assertTrue(localTimeRange.contains(time1));
LocalTime time2 = LocalTime.of(1,20);
Assert.assertTrue(localTimeRange.contains(time2));
LocalTime time3 = LocalTime.of(1,19);
Assert.assertFalse(localTimeRange.contains(time3));
LocalTime time4 = LocalTime.of(9,19);
Assert.assertFalse(localTimeRange.contains(time4));
}
Range
官方的单元测试代码
https://github.com/google/guava/blob/master/guava-tests/test/com/google/common/collect/RangeTest.java
tabnine Range
代码使用范例
https://www.tabnine.com/code/java/classes/com.google.common.collect.Range
Range
还提供了很多方便的函数,还可以对多区间对象进行区交集(com.google.common.collect.Range#isConnected
)、并集(com.google.common.collect.Range#intersection
)等。
此外对于多段区间操作还提供了 com.google.common.collect.RangeSet
类。通过一个类构造出多个区间,然后传入一个值判断是否命中任意一个区间(com.google.common.collect.RangeSet#contains
),是否和另外一个区间有交集(com.google.common.collect.RangeSet#intersects
)等,非常方便。
写一个简单的单测:
public void testLocalTIme(){
List<Range<LocalTime>> ranges = new ArrayList<>();
ranges.add(Range.closed(LocalTime.of(1,5),LocalTime.of(1,15)));
ranges.add(Range.closed(LocalTime.of(8,25),LocalTime.of(9,15)));
ranges.add(Range.closed(LocalTime.of(8,55),LocalTime.of(9,35)));
RangeSet<LocalTime> rangeSet = ImmutableRangeSet.unionOf(ranges);
Assert.assertTrue(rangeSet.contains(LocalTime.of(1,8)));
Assert.assertFalse(rangeSet.contains(LocalTime.of(1,3)));
Assert.assertTrue(rangeSet.contains(LocalTime.of(8,58)));
}
详细使用方式,参考:
https://www.tabnine.com/code/java/classes/com.google.common.collect.RangeSet
还有很多子类型 如 ImmutableRangeSet
,不可变的 RangeSet 帮助我们编码。
com.google.common.collect.ImmutableRangeSetTest#testSingleBoundedRange
public void testSingleBoundedRange() {
ImmutableRangeSet<Integer> rangeSet = ImmutableRangeSet.of(Range.closedOpen(1, 5));
assertThat(rangeSet.asRanges()).contains(Range.closedOpen(1, 5));
assertTrue(rangeSet.intersects(Range.closed(3, 4)));
assertTrue(rangeSet.intersects(Range.closedOpen(0, 2)));
assertTrue(rangeSet.intersects(Range.closedOpen(3, 7)));
assertTrue(rangeSet.intersects(Range.greaterThan(2)));
assertFalse(rangeSet.intersects(Range.greaterThan(7)));
assertTrue(rangeSet.encloses(Range.closed(3, 4)));
assertTrue(rangeSet.encloses(Range.closedOpen(1, 4)));
assertTrue(rangeSet.encloses(Range.closedOpen(1, 5)));
assertFalse(rangeSet.encloses(Range.greaterThan(2)));
assertTrue(rangeSet.contains(3));
assertFalse(rangeSet.contains(5));
assertFalse(rangeSet.contains(0));
RangeSet<Integer> expectedComplement = TreeRangeSet.create();
expectedComplement.add(Range.lessThan(1));
expectedComplement.add(Range.atLeast(5));
assertEquals(expectedComplement, rangeSet.complement());
}
具体大家可以下载源码、查看单元测试案例等,自行学习。
三、总结
建议如果有时间,大家可多看看 Guava 的源码,哪怕只是了解它提供了哪些好用的工具类,也可以帮助我们简化代码,降低出 BUG 的概率。
如果有时间,可以多看看 Guava 中一些核心类的实现原理,并将其思想学习运用到实际的工作开发中。
以上是关于Java 范围比较的推荐姿势的主要内容,如果未能解决你的问题,请参考以下文章