Contents

Guava中Rangeset简介

1. 概述

在本教程中,我们将展示如何使用 Google Guava 的RangeSet接口及其实现。

RangeSet是由零个或多个非空、断开连接的范围组成的集合。将范围添加到可变RangeSet时,任何连接的范围都会合并在一起,而空范围将被忽略。

RangeSet的基本实现是TreeRangeSet

2. Google Guava 的RangeSet

让我们看看如何使用RangeSet类。

2.1. Maven 依赖

让我们首先在pom.xml中添加 Google 的 Guava 库依赖项:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

可以在此处 检查最新版本的依赖项。

3. 创建

让我们探索一些创建RangeSet实例的方法。

首先,我们可以使用TreeRangeSet类的create方法来创建一个可变集:

RangeSet<Integer> numberRangeSet = TreeRangeSet.create();

如果我们已经有集合,请使用TreeRangeSet类的create方法通过传递该集合来创建一个可变集合:

List<Range<Integer>> numberList = Arrays.asList(Range.closed(0, 2));
RangeSet<Integer> numberRangeSet = TreeRangeSet.create(numberList);

最后,如果我们需要创建一个不可变的范围集,请使用ImmutableRangeSet类(创建遵循构建器模式):

RangeSet<Integer> numberRangeSet 
  = new ImmutableRangeSet.<Integer>builder().add(Range.closed(0, 2)).build();

4. 用法

让我们从一个显示RangeSet用法的简单示例开始。

4.1. 添加到范围

我们可以检查提供的输入是否在集合中任何范围项中存在的范围内:

@Test
public void givenRangeSet_whenQueryWithinRange_returnsSucessfully() {
    RangeSet<Integer> numberRangeSet = TreeRangeSet.create();
    numberRangeSet.add(Range.closed(0, 2));
    numberRangeSet.add(Range.closed(3, 5));
    numberRangeSet.add(Range.closed(6, 8));
    assertTrue(numberRangeSet.contains(1));
    assertFalse(numberRangeSet.contains(9));
}

笔记:

  • Range类的closed方法假定整数值的范围在 0 到 2 之间(包括两者)
  • 上例中的Range由整数组成。我们可以使用由任何类型组成的范围,只要它实现了Comparable接口,例如StringCharacter、浮点小数等
  • 对于ImmutableRangeSet,集合中存在的范围项不能与想要添加的范围项重叠。如果发生这种情况,我们会收到IllegalArgumentException
  • RangeSet的范围输入不能为空。如果输入为null,我们将得到一个NullPointerException

4.2. 删除范围

让我们看看如何从RangeSet中删除值:

@Test
public void givenRangeSet_whenRemoveRangeIsCalled_removesSucessfully() {
    RangeSet<Integer> numberRangeSet = TreeRangeSet.create();
    numberRangeSet.add(Range.closed(0, 2));
    numberRangeSet.add(Range.closed(3, 5));
    numberRangeSet.add(Range.closed(6, 8));
    numberRangeSet.add(Range.closed(9, 15));
    numberRangeSet.remove(Range.closed(3, 5));
    numberRangeSet.remove(Range.closed(7, 10));
    assertTrue(numberRangeSet.contains(1));
    assertFalse(numberRangeSet.contains(9));
    assertTrue(numberRangeSet.contains(12));
}

可以看出,删除后我们仍然可以访问集合中剩余的任何范围项中存在的值。

4.3. 范围跨度

现在让我们看看RangeSet的整体跨度是多少:

@Test
public void givenRangeSet_whenSpanIsCalled_returnsSucessfully() {
    RangeSet<Integer> numberRangeSet = TreeRangeSet.create();
    numberRangeSet.add(Range.closed(0, 2));
    numberRangeSet.add(Range.closed(3, 5));
    numberRangeSet.add(Range.closed(6, 8));
    Range<Integer> experienceSpan = numberRangeSet.span();
    assertEquals(0, experienceSpan.lowerEndpoint().intValue());
    assertEquals(8, experienceSpan.upperEndpoint().intValue());
}

4.4. 获取子范围

如果我们希望根据给定的Range获取RangeSet的一部分,我们可以使用subRangeSet方法:

@Test
public void 
  givenRangeSet_whenSubRangeSetIsCalled_returnsSubRangeSucessfully() {
  
    RangeSet<Integer> numberRangeSet = TreeRangeSet.create();
    numberRangeSet.add(Range.closed(0, 2));
    numberRangeSet.add(Range.closed(3, 5));
    numberRangeSet.add(Range.closed(6, 8));
    RangeSet<Integer> numberSubRangeSet 
      = numberRangeSet.subRangeSet(Range.closed(4, 14));
    assertFalse(numberSubRangeSet.contains(3));
    assertFalse(numberSubRangeSet.contains(14));
    assertTrue(numberSubRangeSet.contains(7));
}

4.5. 补法

接下来,让我们使用补码方法获取除RangeSet中的值之外的所有值:

@Test
public void givenRangeSet_whenComplementIsCalled_returnsSucessfully() {
    RangeSet<Integer> numberRangeSet = TreeRangeSet.create();
    numberRangeSet.add(Range.closed(0, 2));
    numberRangeSet.add(Range.closed(3, 5));
    numberRangeSet.add(Range.closed(6, 8));
    RangeSet<Integer> numberRangeComplementSet
      = numberRangeSet.complement();
    assertTrue(numberRangeComplementSet.contains(-1000));
    assertFalse(numberRangeComplementSet.contains(2));
    assertFalse(numberRangeComplementSet.contains(3));
    assertTrue(numberRangeComplementSet.contains(1000));
}

4.6. 与范围相交

最后,当我们想检查RangeSet中存在的范围区间是否与另一个给定范围中的部分或全部值相交时,我们可以使用intersect方法:

@Test
public void givenRangeSet_whenIntersectsWithinRange_returnsSucessfully() {
    RangeSet<Integer> numberRangeSet = TreeRangeSet.create();
    numberRangeSet.add(Range.closed(0, 2));
    numberRangeSet.add(Range.closed(3, 10));
    numberRangeSet.add(Range.closed(15, 18));
    assertTrue(numberRangeSet.intersects(Range.closed(4, 17)));
    assertFalse(numberRangeSet.intersects(Range.closed(19, 200)));
}