Javaで開発をしていると、「数値の範囲に応じて異なる値を割り当てたい」場面によく出会います。たとえば、スコアに応じた成績判定、時間帯ごとの料金設定、年齢別の区分けなどです。
こうした処理をif-else
やswitch
文で実装すると、条件が増えるにつれてコードが煩雑になり、保守も難しくなっていきます。
そこで活躍するのが、GoogleのGuavaライブラリが提供するRangeMap
です。RangeMap
を使えば、「範囲」に対してスマートに値をマッピングでき、ロジックを簡潔かつ見通しよく記述できます。
この記事では、RangeMap
の基本的な使い方から応用例までを、実際のコードとともに分かりやすく解説していきます。
RangeMapとは?
RangeMap<K extends Comparable, V>
は、特定の「範囲(Range<K>)」に対して値(V)を関連付けるためのデータ構造です。範囲は連続する値の区間(例:0〜59、9:00〜18:00など)で表され、個別のキーではなく「区間」を使ってマッピングできるのが特徴です。
通常のMap<K, V>
では、1つのキーにしか値を割り当てられず、範囲ベースの条件分岐はif
やswitch
文に頼る必要があります。
しかしRangeMap
を使えば、たとえば「60〜79点は可」「80〜100点は優」といったように、区間単位で値をマッピングできるため、コードの見通しが大きく改善します。
GuavaにはこのRangeMap
の代表的な実装として、TreeRangeMap
が用意されています。
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
このように、数値や時間など連続性のあるデータに対して直感的に値を割り当てたい場合、RangeMap
は非常に有効な選択肢となります。
Range
自体の詳細については、以下の記事をご覧ください。

RangeMapの基本的な使い方
RangeMap
では、範囲と値をセットで操作します。ここでは、主要なメソッドの使い方と特徴を実例とともに紹介します。
put(Range<K>, V)
:範囲に値を割り当てる
指定した範囲に値を設定します。既存の範囲と重なる場合、その重複部分は自動的に削除されて上書きされます。
rangeMap.put(Range.closed(60, 79), "可");
putCoalescing(Range<K>, V)
:隣接する範囲を自動で結合して割り当てる
put
と似ていますが、前後の隣接または重複する範囲で値が同じ場合、それらを1つの範囲に自動で統合します。
rangeMap.put(Range.closed(0, 9), "低");
rangeMap.put(Range.closed(10, 19), "低");
rangeMap.putCoalescing(Range.closed(19, 29), "低");
// → 結果: [0..9]=低, [10..29]=低
putAll(RangeMap<K, V>)
:他のRangeMapをまとめて追加
別のRangeMap
に登録された範囲と値を一括で追加します。重複する範囲は上書きされるため、マージ前に重複範囲に注意が必要です。
RangeMap<Integer, String> otherRangeMap = TreeRangeMap.create();
otherRangeMap.put(Range.closed(90, 100), "満点");
rangeMap.putAll(otherRangeMap);
get(K)
:値を取得
指定されたキーが属する範囲の値を返します。該当する範囲がなければnull
が返ります。
rangeMap.get(75); // → "可"
getEntry(K)
:範囲と値のペアを取得
指定キーが含まれる範囲とその値をMap.Entry
形式で取得できます。キーの範囲も含めて情報が欲しい場合に便利です。該当する範囲がなければnull
が返ります。
Map.Entry<Range<Integer>, String> entry = rangeMap.getEntry(85);
System.out.println(entry.getKey()); // → [80..100]
System.out.println(entry.getValue()); // → "優"
remove(Range<K>)
:範囲の削除
指定した範囲に一致または部分的に重なるマッピングを削除します。
重なりがある場合は、その範囲だけが切り取られて消えます。
rangeMap.remove(Range.closed(70, 100));
merge(Range<K>, V, BiFunction<V, V, V>)
:値を合成して登録
重複範囲がある場合、既存の値と新しい値を関数で合成し、マッピングに登録します。
ログやラベルのように複数の情報を重ねたいときに便利です。
rangeMap.put(Range.closed(0, 9), "A");
rangeMap.merge(Range.closed(0, 9), "B", (oldVal, newVal) -> oldVal + "+" + newVal);
// → [0..9] = "A+B"
clear()
:全データ削除
マップに登録されたすべての範囲と値を削除します。
rangeMap.clear();
asMapOfRanges()
/ asDescendingMapOfRanges()
:読み取り用のMapを取得
現在の内容を、Range<K>
をキーにしたMap
形式で取得できます。asMapOfRanges()
は昇順、asDescendingMapOfRanges()
は降順になります。読み取り専用で、直接編集はできません。
Map<Range<Integer>, String> ascending = rangeMap.asMapOfRanges();
Map<Range<Integer>, String> descending = rangeMap.asDescendingMapOfRanges();
subRangeMap(Range<K>)
:部分ビューを取得
指定した範囲に含まれるマッピングだけを切り出したライブビューを返します。
このビューでの変更は、元のRangeMap
にも反映されます。
RangeMap<Integer, String> subRangeMap = rangeMap.subRangeMap(Range.closed(60, 90));
span()
:登録済み範囲の全体を取得
現在登録されているすべての範囲を含む、最小~最大までのRange
を返します。
Range<Integer> span = rangeMap.span(); // → 例:[0..100]
空の場合はNoSuchElementException
がスローされるため、必要に応じてasMapOfRanges().isEmpty()
で存在チェックを行ってください。
よくあるユースケース3選
RangeMap
は、単なるコレクションの代替ではなく、「範囲ごとに異なる処理・値を割り当てる」場面で非常に効果を発揮します。ここでは、実際の開発でよくある代表的なユースケースを3つ紹介します。
1. テストスコアに応じた成績の自動判定
スコアの点数に応じて「不可」「可」「優」などの評価を行う処理は、学校や資格試験、社内ツールでも頻繁に登場します。RangeMap
を使えば、条件分岐なしで一発で判定できます。
RangeMap<Integer, String> gradeMap = TreeRangeMap.create();
gradeMap.put(Range.closed(0, 59), "不可");
gradeMap.put(Range.closed(60, 79), "可");
gradeMap.put(Range.closed(80, 100), "優");
System.out.println(gradeMap.get(75)); // → 可
2. 時間帯ごとの料金プランの管理
タクシー、電力、宿泊など、時間帯によって料金が変わるサービスにおいて、RangeMap
は明快かつ変更しやすい料金表を実現できます。
ここでは、3つの時間帯に分けて料金を設定する例を示します。
RangeMap<LocalTime, Integer> pricingMap = TreeRangeMap.create();
pricingMap.put(Range.closedOpen(LocalTime.of(6, 0), LocalTime.of(10, 0)), 1000);
pricingMap.put(Range.closedOpen(LocalTime.of(10, 0), LocalTime.of(14, 0)), 2000);
pricingMap.put(Range.closedOpen(LocalTime.of(14, 0), LocalTime.of(18, 00)), 1500);
System.out.println(pricingMap.get(LocalTime.of(11, 30))); // → 2000
3. IPアドレス範囲によるアクセス制御
特定のIPレンジにアクセス権限を割り当てたい場合、IPアドレスを数値(long)に変換してRangeMap
で扱うと非常に効率的です。
たとえば、社内ネットワークは「internal」、パートナー企業は「trusted」といった区別が簡単に行えます。
RangeMap<Long, String> ipPolicyMap = TreeRangeMap.create();
ipPolicyMap.put(Range.closed(ipToLong("192.168.0.0"), ipToLong("192.168.0.255")), "internal");
ipPolicyMap.put(Range.closed(ipToLong("10.0.0.0"), ipToLong("10.255.255.255")), "trusted");
System.out.println(ipPolicyMap.get(ipToLong("192.168.0.100"))); // → internal
ipToLong
メソッドは、IPv4アドレスをlong
に変換するユーティリティとして実装しておくと便利です。
RangeMapのメリットと注意点
RangeMapを使うメリット
- 条件分岐の簡素化
複雑なif-else
やswitch
文を排除し、宣言的で読みやすいコードが書けます。 - 直感的に範囲を扱える
Range
をキーとしてそのまま使えるため、範囲の定義や見直しが容易です。 - 重複や隣接範囲の自動処理
putCoalescing
やmerge
など、隣接範囲や重複時の処理も柔軟に対応できます。 - ライブビューやMap形式でのアクセス
asMapOfRanges
やsubRangeMap
により、範囲の一覧表示や部分抽出も簡単です。
使用時の注意点
- 重複範囲は上書きされる
put
を使うと、既存の範囲と重なった部分は自動的に削除されるため、意図しない上書きに注意が必要です。 null
値は使用不可RangeMap
にはnull
を値として登録することはできません。必要に応じてプレースホルダ値などで代替しましょう。- 境界の定義に注意
Range.closed
やRange.open
など、開始・終了点の含む/含まないは動作に大きく影響します。要件に応じて正確に使い分けましょう。
まとめ:RangeMapで範囲管理を直感的に
RangeMap
を使えば、「数値や時刻などの連続する範囲」に対して値を割り当てる処理を、簡潔かつ明確に記述できます。
従来なら煩雑なif-else
やswitch
で実装していたロジックも、RangeMap
を使うことで宣言的・構造的に整理でき、メンテナンス性が大きく向上します。
スコア判定や時間帯別処理、IPアドレス制御など、「範囲ベースの条件」を扱うすべての場面で、RangeMap
は強力な武器になります。
コードの見通しや可読性に悩んだら、ぜひ一度RangeMap
の導入を検討してみてください。
柔軟で直感的な範囲管理が、きっとあなたの開発を楽にしてくれるはずです。