Guava类库——Range连续区间

目录

前言

com.google.common.collect.RangeGuava(Google Guava)集合工具库 中的一个类,用来表示一个连续区间(Range)

它最大的作用就是:

用对象的方式描述区间范围,而不是自己写一堆 >=、<=、>< 判断。

例如:

java 复制代码
年龄 >= 18 && 年龄 <= 60

可以写成:

java 复制代码
Range<Integer> range = Range.closed(18, 60);

range.contains(20); // true
range.contains(70); // false

一、引入依赖

Maven:

xml 复制代码
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.2.1-jre</version>
</dependency>

导包:

java 复制代码
import com.google.common.collect.Range;

二、Range 的各种区间

1. 闭区间 a,b

包含左右边界

java 复制代码
Range<Integer> range = Range.closed(1, 10);

等价于:

java 复制代码
1 <= x <= 10

测试:

java 复制代码
range.contains(1);   // true
range.contains(10);  // true
range.contains(5);   // true

2. 开区间 (a,b)

不包含左右边界

java 复制代码
Range<Integer> range = Range.open(1, 10);

等价于:

java 复制代码
1 < x < 10

测试:

java 复制代码
range.contains(1);   // false
range.contains(10);  // false
range.contains(5);   // true

3. 左开右闭 (a,b]

java 复制代码
Range<Integer> range = Range.openClosed(1, 10);

等价于:

java 复制代码
1 < x <= 10

4. 左闭右开 [a,b)

java 复制代码
Range<Integer> range = Range.closedOpen(1, 10);

等价于:

java 复制代码
1 <= x < 10

三、无穷区间

1.大于

java 复制代码
Range<Integer> range = Range.greaterThan(10);

等价:

java 复制代码
x > 10

2.大于等于

java 复制代码
Range<Integer> range = Range.atLeast(10);

等价:

java 复制代码
x >= 10

3.小于

java 复制代码
Range<Integer> range = Range.lessThan(10);

等价:

java 复制代码
x < 10

4.小于等于

java 复制代码
Range<Integer> range = Range.atMost(10);

等价:

java 复制代码
x <= 10

4.全部范围

java 复制代码
Range<Integer> range = Range.all();

等价:

java 复制代码
(-∞,+∞)

四、判断元素是否在区间内

最常用的方法:

java 复制代码
contains()

例如:

java 复制代码
Range<Integer> range = Range.closed(1, 100);

System.out.println(range.contains(50));

输出:

java 复制代码
true

五、判断一个区间是否包含另一个区间

java 复制代码
Range<Integer> range1 = Range.closed(1, 100);

Range<Integer> range2 = Range.closed(20, 50);

System.out.println(range1.encloses(range2));

输出:

java 复制代码
true

因为:

text 复制代码
[1,100] 完全包含 [20,50]

六、区间交集

假设:

java 复制代码
[1,10]
[5,15]

求交集:

java 复制代码
Range<Integer> r1 = Range.closed(1,10);
Range<Integer> r2 = Range.closed(5,15);

Range<Integer> intersection = r1.intersection(r2);

System.out.println(intersection);

输出:

java 复制代码
[5..10]

七、区间并集

java 复制代码
Range<Integer> r1 = Range.closed(1,10);
Range<Integer> r2 = Range.closed(5,15);

Range<Integer> span = r1.span(r2);

System.out.println(span);

输出:

java 复制代码
[1..15]

八、判断两个区间是否有重叠

java 复制代码
Range<Integer> r1 = Range.closed(1,10);
Range<Integer> r2 = Range.closed(5,15);

boolean connected = r1.isConnected(r2);

结果:

java 复制代码
true

因为:

text 复制代码
[1,10]
[5,15]

有公共部分。


如果:

java 复制代码
Range<Integer> r1 = Range.closed(1,10);
Range<Integer> r2 = Range.closed(20,30);
r1.isConnected(r2);

结果:

java 复制代码
false

九、获取边界值

java 复制代码
Range<Integer> range = Range.closed(10,20);

获取下界:

java 复制代码
range.lowerEndpoint();

结果:

java 复制代码
10

获取上界:

java 复制代码
range.upperEndpoint();

结果:

java 复制代码
20

十、LocalDate 处理示例

对于 Range<LocalDate>,Guava 原生提供了:

  • 交集
  • 并集

但是:

  • 差集没有直接提供

因为两个区间相减后可能得到 0个、1个或2个区间 ,结果不再是一个单独的 Range

1.交集

假设:

tex 复制代码
A = [2026-01-01,2026-03-31]

B = [2026-02-01,2026-04-30]

图示:

tex 复制代码
A: 2026-01-01 -------------------- 2026-03-31

B:             2026-02-01 -------------------- 2026-04-30

交集:

tex 复制代码
[2026-02-01,2026-03-31]

代码:

java 复制代码
        Range<LocalDate> a = Range.closed(LocalDate.of(2026, 1, 1), LocalDate.of(2026, 3, 31));

        Range<LocalDate> b = Range.closed(LocalDate.of(2026, 2, 1), LocalDate.of(2026, 4, 30));

        Range<LocalDate> intersection = a.intersection(b);

        System.out.println(intersection);

输出:

tex 复制代码
[2026-02-01..2026-03-31]

2.并集

还是上面的两个区间:

tex 复制代码
A = [2026-01-01,2026-03-31]

B = [2026-02-01,2026-04-30]

并集:

tex 复制代码
[2026-01-01,2026-04-30]

代码:

java 复制代码
        Range<LocalDate> a = Range.closed(LocalDate.of(2026, 1, 1), LocalDate.of(2026, 3, 31));

        Range<LocalDate> b = Range.closed(LocalDate.of(2026, 2, 1), LocalDate.of(2026, 4, 30));

		Range<LocalDate> union = a.span(b);

		System.out.println(union);

输出:

tex 复制代码
[2026-01-01..2026-04-30]

注意

求并集使用span() 时不要求区间重叠。

例如:

java 复制代码
		Range<LocalDate> a = Range.closed(LocalDate.of(2026,1,1), LocalDate.of(2026,1,31));

		Range<LocalDate> b = Range.closed(LocalDate.of(2026,3,1), LocalDate.of(2026,3,31));

		Range<LocalDate> union = a.span(b);

		System.out.println(union);

结果:

tex 复制代码
[2026-01-01..2026-03-31]

即使中间二月份是空的。

3.差集(A - B)

假设:

tex 复制代码
A = [2026-01-01,2026-03-31]

B = [2026-02-01,2026-02-28]

图示:

tex 复制代码
A:

|-----------------------------|

B:

      |-----------|

差集:

tex 复制代码
[2026-01-01,2026-01-31]

[2026-03-01,2026-03-31]

结果变成两个区间。

代码:

java 复制代码
public class MainServer {
    public static void main(String[] args) throws Exception {
        Range<LocalDate> a = Range.closed(LocalDate.of(2026,1,1), LocalDate.of(2026,3,31));

        Range<LocalDate> b = Range.closed(LocalDate.of(2026,2,1), LocalDate.of(2026,2,28));

        List<Range<LocalDate>> result = subtract(a, b);

        result.forEach(System.out::println);
    }

    public static List<Range<LocalDate>> subtract(Range<LocalDate> source, Range<LocalDate> remove) {
        List<Range<LocalDate>> result = new ArrayList<>();

        if (!source.isConnected(remove)) {
            result.add(source);
            return result;
        }

        Range<LocalDate> intersection = source.intersection(remove);
        // 左边剩余部分
        if (source.lowerEndpoint().isBefore(intersection.lowerEndpoint())) {
            result.add(
                    Range.closed(source.lowerEndpoint(), intersection.lowerEndpoint().minusDays(1)));
        }

        // 右边剩余部分
        if (source.upperEndpoint().isAfter(intersection.upperEndpoint())) {
            result.add(
                    Range.closed(intersection.upperEndpoint().plusDays(1), source.upperEndpoint()));
        }
        return result;
    }
}

输出:

java 复制代码
[2026-01-01..2026-01-31]
[2026-03-01..2026-03-31]

总结

Range<T> 本质上就是:

java 复制代码
把
x > 1
x >= 1
1 <= x <= 10
5 < x < 20

这种范围条件封装成对象

最常用的方法:

java 复制代码
Range.closed(a,b)       // [a,b]
Range.open(a,b)         // (a,b)
Range.atLeast(a)        // >=a
Range.atMost(a)         // <=a

contains(x)            // 判断值是否在范围内

lowerEndpoint()        // 下界
upperEndpoint()        // 上界

intersection()         // 交集
span()                 // 并集
isConnected()          // 是否重叠
encloses()             // 是否包含
相关推荐
武子康1 小时前
Java-17 深入浅出MyBatis Mapper Proxy 源码解析:从 getMapper 到 invoke 的完整链路
java·后端
菜菜的顾清寒1 小时前
力扣HOT(100)54多维动态规划-最长公共子序列
算法·leetcode·动态规划
plainGeekDev1 小时前
CountDownTimer → Flow
android·java·kotlin
心之伊始1 小时前
Java 后端 AI 应用网关实战:多模型路由、Fallback、超时和可观测性设计
java·spring boot·大模型·架构设计·ai网关
随意起个昵称1 小时前
线性dp-LIS题目3(合唱队形)
算法
小六学编程1 小时前
二分查找详解:从普通二分到左右边界
算法·c/c++
wayz111 小时前
Volume:PVO(百分比成交量震荡指标)技术指标详解
算法·金融·数据分析·量化交易·特征工程
毕竟是shy哥1 小时前
PromptHash:基于亲和提示协同学习的自适应哈希检索跨模态算法
学习·算法·哈希算法
甄心爱学习1 小时前
【项目实训(个人12)】
人工智能·python·算法