นากกายกำลังเลื่อนตัวชี้ข้ามแถวกระเบื้องไปยังหมุดที่ถูกยกขึ้นถัดไป

Java BitSet.nextSetBit: เดินเฉพาะตำแหน่งที่เปิดโดยไม่สแกนทุกช่อง

พี่โชว์Engineering

ถ้ามี flag หลายหมื่นตำแหน่งแต่เปิดเพียงไม่กี่จุด การวน for ตรวจทุก index ทำงานเกินจำเป็นครับ BitSet.nextSetBit(fromIndex) กระโดดไปยัง bit ที่เป็น true ถัดไปและคืน -1 เมื่อหมด

WHY: ให้โครงสร้างบอกตำแหน่งที่มีงาน

BitSet เก็บ flag แบบกะทัดรัดและทำ word-level operations ภายใน การเดินเฉพาะ bit ที่เปิดเหมือนเจ้าหน้าที่ข้ามเก้าอี้ว่างไปยังป้ายเขียวถัดไป เหมาะกับ permission bitmap, dirty indexes และ sparse feature set ที่ domain ระบุด้วยเลขตำแหน่งอยู่แล้ว

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

public class Main {
  static List<Integer> enabledIndexes(BitSet bits) {
    List<Integer> indexes = new ArrayList<>();
    for (int bit = bits.nextSetBit(0); bit >= 0; bit = bits.nextSetBit(bit + 1)) {
      indexes.add(bit);
      if (bit == Integer.MAX_VALUE) break;
    }
    return indexes;
  }

  public static void main(String[] args) {
    BitSet bits = new BitSet();
    bits.set(2);
    bits.set(5);
    bits.set(9);

    assert enabledIndexes(bits).equals(List.of(2, 5, 9));
    assert enabledIndexes(new BitSet()).isEmpty();
    assert bits.cardinality() == 3;
    assert bits.stream().boxed().toList().equals(List.of(2, 5, 9));
  }
}

nextSetBit(bit + 1) มี edge ที่ Integer.MAX_VALUE; เอกสารแนะนำ guard เพื่อไม่ให้บวกแล้ว overflow แม้ BitSet ปกติแทบไม่ใหญ่ถึงจุดนั้น ถ้าต้องการ stream API, bits.stream() คืน IntStream ของตำแหน่งที่เปิด อ่านง่ายสำหรับ pipeline สั้น แต่ loop เหมาะเมื่อมี early exit หรือ state ระหว่างทาง

อย่าเอา database ID ที่ sparse และโตไม่จำกัดมาใช้เป็น index โดยตรง เพราะ bit สูงมากทำให้ storage โตตามตำแหน่งสูงสุด แยก compact ordinal mapping หรือใช้ set เมื่อ identity ไม่ใช่ช่วงเลขหนาแน่น

Recap

  • ใช้ nextSetBit เพื่อเดินเฉพาะ flag ที่เปิด
  • เริ่มจากศูนย์และขยับด้วย bit + 1
  • guard ค่า Integer.MAX_VALUE หาก loop ทั่วไปต้องสมบูรณ์
  • ใช้ Set เมื่อ key ไม่ใช่ compact integer index