นากแม่น้ำต้นฉบับกำลังเลื่อนกรอบหน้าต่างเหนือแถวบัตรคีย์ที่เรียงจากซ้ายไปขวาบนโต๊ะสีมิ้นต์

Java NavigableMap.subMap: เปิดหน้าต่างช่วงคีย์แบบ live view

พี่โชว์Engineering

ข้อมูลที่เรียงตามคีย์อยู่แล้วไม่จำเป็นต้อง filter ทั้ง map เพื่อหาเฉพาะช่วงครับ NavigableMap.subMap(from, fromInclusive, to, toInclusive) คืน view ของช่วงคีย์ พร้อมบอกชัดว่าขอบซ้ายและขวารวมอยู่หรือไม่

WHY: range semantics ต้องอยู่ใน API

ช่วงเวลา [09:00, 10:00) หมายถึงรวมเก้าโมงแต่ไม่รวมสิบโมง รูปแบบครึ่งเปิดนี้ช่วยต่อช่วงโดยไม่ซ้ำเหตุการณ์ตรงรอยต่อ ถ้าเขียน if เองหลายที่ เครื่องหมาย < กับ <= หลุดได้ง่าย subMap ย้ายกติกาไปไว้กับโครงสร้างที่เรียงคีย์และทดสอบได้

HOW: ใช้ view และเห็นผลที่ map ต้นทาง

import java.util.NavigableMap;
import java.util.TreeMap;

public class Main {
  public static void main(String[] args) {
    NavigableMap<Integer, String> timeline = new TreeMap<>();
    timeline.put(900, "open");
    timeline.put(930, "sync");
    timeline.put(1000, "handoff");
    timeline.put(1030, "review");

    NavigableMap<Integer, String> morning =
        timeline.subMap(900, true, 1000, false);

    assert morning.size() == 2 : morning;
    assert morning.firstKey() == 900;
    assert morning.lastKey() == 930;

    morning.put(945, "checkpoint");
    assert timeline.get(945).equals("checkpoint");

    boolean rejected = false;
    try {
      morning.put(1030, "outside");
    } catch (IllegalArgumentException expected) {
      rejected = true;
    }
    assert rejected : "view must reject keys outside its range";
  }
}

คำว่า view สำคัญมาก การเพิ่ม ลบ หรือแก้ค่าผ่านช่วงจะเปลี่ยน map ต้นทาง และการแก้ต้นทางก็สะท้อนใน view ถ้าต้องส่ง snapshot ให้โค้ดอื่น ให้ copy เป็น new TreeMap<>(view) หรือ immutable representation แทน

เลือกขอบให้สอดคล้องกับโดเมน

ช่วงเวลาและ pagination มักใช้ขอบขวา exclusive เพื่อไม่ซ้ำรายการ แต่ช่วงคะแนนอาจรวมทั้งสองขอบ เขียนชื่อ helper ให้เห็น semantics เช่น eventsFromInclusiveToExclusive และ test จุดขอบโดยตรง อย่าทดสอบแต่ค่ากลางช่วง

Recap

  • ระบุ inclusive/exclusive ทั้งสองขอบ
  • จำว่า subMap เป็น live view ไม่ใช่ copy
  • copy ก่อนส่งข้าม ownership boundary
  • ทดสอบคีย์ตรงขอบและคีย์นอกช่วง