เชี่ยวชาญ Java Collections
นักพัฒนาส่วนใหญ่มักจะเลือกใช้ ArrayList หรือ HashSet เป็นค่าเริ่มต้น ซึ่งเพียงพอสำหรับงานง่ายๆ แต่จะเริ่มมีปัญหาเมื่อคุณต้องการความเร็วหรือการรองรับข้อมูลขนาดใหญ่ (scale)
ครั้งหนึ่งผมเคยสร้างตารางคะแนนเกม (leaderboard) โดยใช้ ArrayList ธรรมดา ผมต้องทำการเรียงลำดับใหม่ทุกครั้งที่มีการเปลี่ยนคะแนน ผลคือ UI ค้างอยู่ตลอดเวลา ผมกำลังต่อสู้กับตัวภาษาแทนที่จะใช้งานมันอย่างถูกวิธี
เลิกใช้เครื่องมือที่ไม่เหมาะสม แล้วหันมาใช้ Collection เฉพาะทาง 3 ชนิดนี้ เพื่อเขียนโค้ดที่เร็วขึ้นและสะอาดขึ้น
- EnumSet สำหรับ Enum Constants
หากคุณใช้ HashSet สำหรับ enums คุณจะต้องเสียต้นทุนด้านประสิทธิภาพ (performance tax) เพราะทุกครั้งที่มีการเพิ่มข้อมูล enum จะถูกทำ boxing ให้กลายเป็น object ซึ่งเป็นการเพิ่ม overhead โดยไม่จำเป็น
EnumSet ใช้ bit vector และทำการตรวจสอบโดยใช้คำสั่ง CPU เพียงคำสั่งเดียว
- ใช้เมื่อคุณมีชุดค่า enum ที่แน่นอน
- ช่วยลดการทำงานของ garbage collection
- ช่วยเพิ่มความเร็วได้ถึง 10 เท่าในลูปที่ทำงานซ้ำๆ (tight loops)
Before:
Set<Ability> abilities = new HashSet<>();
abilities.add(Ability.FIRE);
After:
EnumSet<Ability> abilities = EnumSet.of(Ability.FIRE);
- NavigableSet สำหรับการค้นหาแบบช่วง (Range Queries)
การวนลูปผ่าน list ที่เรียงลำดับแล้วด้วยตัวเองเพื่อหาช่วงข้อมูลนั้นทั้งช้าและเสี่ยงต่อข้อผิดพลาด คุณมักจะเจอบั๊กประเภท off-by-one (การนับตำแหน่งผิดไปหนึ่ง)
NavigableSet จะรักษาข้อมูลของคุณให้เรียงลำดับโดยอัตโนมัติ และให้ประสิทธิภาพการค้นหา subset ในระดับ O(log n)
- ใช้สำหรับตารางคะแนนสูงสุดหรือช่วงราคา
- ใช้
headSet()หรือsubSet()เพื่อดึงข้อมูลในช่วงที่ต้องการ - ช่วยลดความจำเป็นในการเขียนโค้ดเรียงลำดับข้อมูลด้วยตัวเอง
Before:
Collections.sort(scores);
List<Integer> topTen = scores.subList(size - 10, size);
After:
NavigableSet<Integer> scores = new TreeSet<>(Comparator.reverseOrder());
scores.add(1542);
NavigableSet<Integer> topTen = scores.headSet(scores.first(), true).stream().limit(10).collect(Collectors.toCollection(TreeSet::new));
- CopyOnWriteArrayList สำหรับ List ที่เน้นการอ่านข้อมูลเป็นหลัก (Read-Heavy Lists)
การใช้ synchronized blocks กับ ArrayList จะทำให้การอ่านข้อมูลทุกครั้งช้าลง และยังอาจทำให้เกิด ConcurrentModificationException หากมี thread หนึ่งกำลังเขียนข้อมูลในขณะที่อีก thread หนึ่งกำลังอ่านอยู่
CopyOnWriteArrayList จะสร้างสำเนาใหม่ของ array ทุกครั้งที่มีการเขียนข้อมูล โดยผู้ที่อ่านข้อมูลจะมองเห็นข้อมูลในรูปแบบ snapshot ของ array นั้น
- ใช้สำหรับ event listeners หรือการตั้งค่าคอนฟิกูเรชัน (configuration settings)
- ใช้เมื่อมีการอ่านข้อมูลบ่อยกว่าการเขียนข้อมูลมาก
- ช่วยให้สามารถอ่านข้อมูลได้โดยไม่ต้องใช้ lock (lock-free reads)
Before:
List<String> log = Collections.synchronizedList(new ArrayList<>());
// Iterating here can crash if a writer joins in.
After:
CopyOnWriteArrayList<String> log = new CopyOnWriteArrayList<>();
// Iteration is safe and never crashes.
เลิกใช้คอลเลกชันเดิมๆ เพียงแค่สองอย่างเป็นค่าเริ่มต้นเสียที เลือกเครื่องมือที่เหมาะสมกับรูปแบบข้อมูลของคุณ
ที่มา: https://dev.to/timevolt/the-java-collections-force-mastering-the-hidden-gems-like-a-jedi-4438