เมื่อเร็ว ๆ นี้ Jon Skeet ได้ยกหัวข้อการเขียนโปรแกรมที่น่าสนใจในบล็อกของเขา: "มีช่องว่างในสิ่งที่เป็นนามธรรมของฉัน Liza ที่รัก Liza ที่รัก" (เน้นเพิ่มเติม):
ฉันมีชุด - อันที่
HashSet
จริง ฉันต้องการลบบางรายการออกจากมัน ... และหลายรายการอาจไม่มีอยู่จริง อันที่จริงในกรณีทดสอบของเราไม่มีรายการใดในคอลเล็กชัน "การลบ" ที่จะอยู่ในชุดเดิม ฟังดู - และเป็นรหัสที่ง่ายมาก ท้ายที่สุดเราต้องSet<T>.removeAll
ช่วยเราใช่มั้ย?เราระบุขนาดของชุด "แหล่งที่มา" และขนาดของคอลเล็กชัน "การลบ" ในบรรทัดคำสั่งและสร้างทั้งสองชุด ชุดแหล่งที่มามีเฉพาะจำนวนเต็มที่ไม่เป็นลบ ชุดการลบมีเฉพาะจำนวนเต็มลบ เราวัดว่าต้องใช้เวลานานแค่ไหนในการลบองค์ประกอบทั้งหมดโดยใช้
System.currentTimeMillis()
ซึ่งไม่ใช่นาฬิกาจับเวลาที่แม่นยำที่สุดในโลก แต่เพียงพอแล้วในกรณีนี้อย่างที่คุณเห็น นี่คือรหัส:
import java.util.*; public class Test { public static void main(String[] args) { int sourceSize = Integer.parseInt(args[0]); int removalsSize = Integer.parseInt(args[1]); Set<Integer> source = new HashSet<Integer>(); Collection<Integer> removals = new ArrayList<Integer>(); for (int i = 0; i < sourceSize; i++) { source.add(i); } for (int i = 1; i <= removalsSize; i++) { removals.add(-i); } long start = System.currentTimeMillis(); source.removeAll(removals); long end = System.currentTimeMillis(); System.out.println("Time taken: " + (end - start) + "ms"); } }
เริ่มต้นด้วยการทำให้งานง่าย: ชุดแหล่งที่มาของรายการ 100 รายการและ 100 รายการที่จะลบ:
c:UsersJonTest>java Test 100 100 Time taken: 1ms
โอเคเราไม่ได้คาดหวังว่ามันจะช้า…เห็นได้ชัดว่าเราสามารถเพิ่มขึ้นเล็กน้อย แล้วแหล่งที่มาของหนึ่งล้านรายการและ 300,000 รายการที่จะลบ?
c:UsersJonTest>java Test 1000000 300000 Time taken: 38ms
อืม. ยังคงดูค่อนข้างเร็ว ตอนนี้ฉันรู้สึกว่าฉันโหดร้ายไปหน่อยขอให้มันลบทั้งหมด มาทำให้ง่ายขึ้นเล็กน้อย - รายการต้นทาง 300,000 รายการและการลบ 300,000 รายการ:
c:UsersJonTest>java Test 300000 300000 Time taken: 178131ms
ขออนุญาต? เกือบสามนาที ? อ๊ะ! แน่นอนว่ามันควรจะง่ายกว่าที่จะลบรายการออกจากคอลเลกชันขนาดเล็กกว่าที่เราจัดการใน 38ms?
มีใครอธิบายได้ไหมว่าเหตุใดจึงเกิดขึ้น ทำไมHashSet<T>.removeAll
วิธีช้าจัง