นากต้นฉบับคัดผลไม้ช้ำสีอำพันออกจากตะกร้าเขียวโดยไม่เทของทั้งตะกร้า

Java removeIf: ล้างสมาชิกใน collection โดยไม่แก้ระหว่าง for-each

พี่โชว์Engineering

การลบสมาชิกจาก collection ภายใน enhanced for มักจบด้วย ConcurrentModificationException Collection.removeIf รวมเงื่อนไขกับการลบผ่าน iterator ของ collection อย่างถูกสัญญา เหมาะกับการ cleanup แบบ in-place ครับ

WHY: ให้ตะกร้าจัดการช่องว่างเอง

เหมือนคัดผลไม้ช้ำออกจากตะกร้า เราบอกเกณฑ์ว่าใบไหนต้องออก ส่วนตะกร้ารับผิดชอบการเลื่อนของที่เหลือ ไม่ควรดึงของออกระหว่างเดินนับด้วยกลไกคนละชุด

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

public class Main {
    record Coupon(String code, LocalDate expiresOn) {}

    static int removeExpired(List<Coupon> coupons, LocalDate today) {
        int before = coupons.size();
        coupons.removeIf(coupon -> coupon.expiresOn().isBefore(today));
        return before - coupons.size();
    }

    public static void main(String[] args) {
        List<Coupon> coupons = new ArrayList<>(List.of(
            new Coupon("OLD", LocalDate.of(2026, 10, 11)),
            new Coupon("TODAY", LocalDate.of(2026, 10, 12)),
            new Coupon("NEXT", LocalDate.of(2026, 10, 13))
        ));
        assert removeExpired(coupons, LocalDate.of(2026, 10, 12)) == 1;
        assert coupons.stream().map(Coupon::code).toList().equals(List.of("TODAY", "NEXT"));
        assert removeExpired(coupons, LocalDate.of(2026, 10, 12)) == 0;
        System.out.println("removeIf cleanup verified");
    }
}

HOW: API นี้แก้ collection เดิม

removeIf คืน true เมื่อมีสมาชิกถูกลบ แต่ไม่ได้คืนรายการที่หายไป ตัวอย่างนับขนาดก่อนและหลัง ถ้าต้อง audit รายการที่ลบ ให้ snapshot เฉพาะรายการนั้นก่อน หรือออกแบบ operation ให้ repository บันทึก event พร้อม transaction

Collection ต้องรองรับ mutation เช่น new ArrayList<>(...); List.of(...) โดยตรงเป็น immutable และจะโยน UnsupportedOperationException เมื่อมีสมาชิกต้องลบ Predicate ควรไม่มี side effect เพราะ implementation อาจมีพฤติกรรม iteration ต่างกัน และห้ามแก้ collection เดียวกันจาก thread อื่นโดยไม่มี synchronization

Recap

  • ใช้ removeIf แทนลบจาก enhanced for-loop
  • ยืนยันว่า collection mutable
  • ทำ predicate ให้บริสุทธิ์และรวดเร็ว
  • เก็บ audit ก่อนลบถ้าต้องรู้รายการที่หาย
  • ทดสอบ boundary เช่นหมดอายุ “ก่อนวันนี้” กับ “วันนี้”