1. 概要

このチュートリアルでは、GoogleGuavaのRangeSetインターフェースとその実装の使用方法を示します。

RangeSet は、0個以上の空でない切断された範囲で構成されるセットです。 可変RangeSetに範囲を追加すると、接続されているすべての範囲がマージされ、空の範囲は無視されます。

RangeSetの基本的な実装はTreeRangeSetです。

2. GoogleGuavaの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のインスタンスを作成する方法のいくつかを見てみましょう。

まず、クラスTreeRangeSetcreateメソッドを使用して、可変セットを作成できます。

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

すでにコレクションが配置されている場合は、クラスTreeRangeSetcreateメソッドを使用して、そのコレクションを渡すことで可変セットを作成します。

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は、整数で構成されています。 String Character 、浮動小数点小数などの Compareable インターフェースを実装している限り、任意のタイプの範囲を使用できます。
  • ImmutableRangeSet の場合、セットに存在する範囲アイテムは、追加したい範囲アイテムとオーバーラップできません。 その場合、IllegalArgumentExceptionが発生します
  • RangeSetへの範囲入力をnullにすることはできません。 入力が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. 補完方法

次に、 complement メソッドを使用して、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)));
}

5. 結論

このチュートリアルでは、いくつかの例を使用して、GuavaライブラリのRangeSetを説明しました。 RangeSet は主に、値がセットに存在する特定の範囲内にあるかどうかを確認するために使用されます。

これらの例の実装は、 GitHubプロジェクトにあります。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。