0Pricing
Java Academy · レッスン

サブマップと範囲ビュー

ソート済みマップで範囲検索を行うために、subMap、headMap、tailMap のビューを抽出します。

「サブマップと範囲ビュー」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

TreeMap の範囲ビュー

TreeMap の subMap、headMap、tailMap は、元のマップに連動するビューを返します。元のマップの変更はビューに反映され、その逆も同様です。ビューを通じた変更も元のマップに反映されます。

TreeMap<Integer, String> map = new TreeMap<>();
for (int i = 1; i <= 10; i++) map.put(i * 10, "item" + i);

var view = map.subMap(30, 70); // [30, 70)
System.out.println(view); // {30=item3, 40=item4, 50=item5, 60=item6}

map.put(45, "new"); // also visible through view!
System.out.println(view.containsKey(45)); // true

headMap: 境界未満のキー

headMap(toKey) は、キーが toKey より厳密に小さいすべてのエントリを返します。境界値を含めるには、包含バリエーションの headMap(toKey, true) を使用します。

TreeMap<String, Integer> words = new TreeMap<>();
"banana cherry apple date elderberry".chars()
    .mapToObj(c -> String.valueOf((char)c)).distinct()
    .forEach(w -> words.put(w, w.length()));
// Actually let's use real words:
TreeMap<String, Integer> wc = new TreeMap<>();
wc.put("apple",5); wc.put("banana",6); wc.put("cherry",6); wc.put("date",4);

System.out.println(wc.headMap("cherry")); // {apple=5, banana=6}

tailMap: 境界以上のキー

tailMap(fromKey) は、キーが fromKey 以上のすべてのエントリを返します。

TreeMap<Integer, String> grades = new TreeMap<>();
grades.put(50,"F"); grades.put(60,"D"); grades.put(70,"C"); grades.put(80,"B"); grades.put(90,"A");

// All passing grades (>= 60)
var passing = grades.tailMap(60);
System.out.println(passing); // {60=D, 70=C, 80=B, 90=A}

包含境界を指定した subMap

4 引数の subMap(from, fromInclusive, to, toInclusive) を使うと、境界値を含めるかどうかを完全に制御できます:

TreeMap<Integer, String> map = new TreeMap<>();
for (int i = 10; i <= 100; i += 10) map.put(i, "v"+i);

// [30, 60] — both inclusive
System.out.println(map.subMap(30, true, 60, true));
// {30=v30, 40=v40, 50=v50, 60=v60}

// (30, 60) — both exclusive
System.out.println(map.subMap(30, false, 60, false));
// {40=v40, 50=v50}

ビューを通じた変更

subMap ビューに対する put/remove 操作は元のマップに反映され、その逆も同様です。ビューの範囲外のキーを挿入しようとすると、例外がスローされます。

TreeMap<Integer, String> map = new TreeMap<>();
for (int i = 1; i <= 5; i++) map.put(i * 10, "v" + i);

var view = map.subMap(20, 40); // [20, 40)
view.remove(20); // removes from both view and original map
System.out.println(map.containsKey(20)); // false

// This would throw IllegalArgumentException:
// view.put(50, "out of range");

ユースケース: ログの範囲クエリ

TreeMap の範囲ビューを使用して、2 つのタイムスタンプの間にあるすべてのログエントリを取得します:

import java.time.*;
TreeMap<LocalDateTime, String> logs = new TreeMap<>();
logs.put(LocalDateTime.of(2024,1,1,8,0), "Server start");
logs.put(LocalDateTime.of(2024,1,1,10,0), "Request spike");
logs.put(LocalDateTime.of(2024,1,1,14,0), "Maintenance");
logs.put(LocalDateTime.of(2024,1,1,18,0), "Server stop");

var morning = logs.subMap(
    LocalDateTime.of(2024,1,1,8,0), true,
    LocalDateTime.of(2024,1,1,12,0), false
);
morning.forEach((t,m) -> System.out.println(t+" : "+m));

ユースケース: 価格範囲の検索

TreeMap のキーに価格を使用して、指定した価格範囲の商品をすべて見つけます:

TreeMap<Double, String> products = new TreeMap<>();
products.put(9.99, "Pen");
products.put(24.99, "Book");
products.put(49.99, "Headphones");
products.put(299.99, "Tablet");

double min = 10.0, max = 100.0;
var affordable = products.subMap(min, true, max, true);
affordable.forEach((p,n) -> System.out.println(n+" $"+p));
// Book $24.99, Headphones $49.99

降順の subMap

ビューに descendingMap() を連結して、逆順のナビゲーションを行います:

TreeMap<Integer, String> map = new TreeMap<>();
for (int i = 10; i <= 100; i += 10) map.put(i, "v"+i);

// Get [40, 80] in descending order
map.subMap(40, true, 80, true)
   .descendingMap()
   .forEach((k,v) -> System.out.println(k + "=" + v));
// 80=v80, 70=v70, 60=v60, 50=v50, 40=v40

NavigableMap インターフェース

NavigableMap は SortedMap を拡張し、ceiling/floor/higher/lower によるキーのナビゲーションと降順ビューを追加します。TreeMap が最も一般的な実装であり、ConcurrentSkipListMap はスレッドセーフな代替実装です。

ビューのパフォーマンス

サブマップビューの操作(get、put、containsKey)は、元の TreeMap と同じ O(log n) です。ビューの作成自体は O(1) で、コピーは行われません。ビュー内の n 個のキーを範囲走査する場合は O(log N + n) です。N はマップ全体のサイズを表します。

落とし穴: 古いビュー

ビューは元のマップに連動しているため、元のマップがクリアされると、ビューが空になったり例外をスローしたりすることがあります。ビューが常に最新の状態に連動することを明記し、意図したライフサイクルを超えて保持しないでください。

TreeMap<Integer, String> map = new TreeMap<>();
map.put(10, "a"); map.put(20, "b"); map.put(30, "c");

var view = map.subMap(10, 30);
map.clear(); // view becomes empty
System.out.println(view.size()); // 0 — but no exception

確認問題

キーが {10,20,30,40,50,60,70,80} の TreeMap に対して map.subMap(30, false, 70, true) を呼び出します。結果にはどのキーが含まれますか?

まとめ: サブマップと範囲ビュー

要点:

  • subMap、headMap、tailMap は、コピーを作成せずに元のマップに連動するライブビューを返します
  • ビューの変更は元のマップに反映され、その逆も同様です
  • 境界を完全に制御するには 4 引数の subMap(from, fromInclusive, to, toInclusive) を使用します
  • ビューを通じて範囲外のキーを put すると IllegalArgumentException がスローされます
  • 範囲走査の計算量: O(log N + n)

よくある質問

「サブマップと範囲ビュー」レッスンは無料ですか?

はい。「サブマップと範囲ビュー」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「サブマップと範囲ビュー」で何を学びますか?

ソート済みマップで範囲検索を行うために、subMap、headMap、tailMap のビューを抽出します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「サブマップと範囲ビュー」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. TreeMap:ソートされたキーと値のペア
  2. サブマップと範囲ビュー
  3. TreeSet と NavigableSet
  4. Tree コレクションのカスタム順序
← Java Academyに戻る