在平时工作开发过程中,很容易遇到判断某个值是否在某个范围的场景。
如需要校验某个日期是否在某个范围;需要校验某个版本号是否在某个区间;需要校验某个时间点是否在某个时间段内;判断某个人是否属于某个年龄段;判断某个用户的积分是否属于某个等级的区间等。
前一阵子,技术群里有哥们就提了类似的一个问题:
判断当前时间是否在周期的时间段里面有什么好的办法吗 比如 当前时间是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
com.google.guava guava31.0.1-jre 31.0.1-android
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); // 构造闭区间 RangelocalTimeRange = 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> 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 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 帮助我们编码。
单测代码
https://github.com/google/guava/blob/master/guava-tests/test/com/google/common/collect/ImmutableRangeSetTest.java
com.google.common.collect.ImmutableRangeSetTest#testSingleBoundedRange
public void testSingleBoundedRange() { ImmutableRangeSetrangeSet = 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 expectedComplement = TreeRangeSet.create(); expectedComplement.add(Range.lessThan(1)); expectedComplement.add(Range.atLeast(5)); assertEquals(expectedComplement, rangeSet.complement()); }
具体大家可以下载源码、查看单元测试案例等,自行学习。
三、总结建议如果有时间,大家可多看看 Guava 的源码,哪怕只是了解它提供了哪些好用的工具类,也可以帮助我们简化代码,降低出 BUG 的概率。
如果有时间,可以多看看 Guava 中一些核心类的实现原理,并将其思想学习运用到实际的工作开发中。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)