Jon Skeet gần đây đã nêu ra một chủ đề lập trình thú vị trên blog của mình: "Có một lỗ hổng trong sự trừu tượng của tôi, Liza thân mến, Liza thân mến" (nhấn mạnh thêm):
Tôi có một bộ - một
HashSet
, trên thực tế. Tôi muốn xóa một số mục khỏi nó… và nhiều mục có thể không tồn tại. Trên thực tế, trong trường hợp thử nghiệm của chúng tôi, không có mục nào trong bộ sưu tập "loại bỏ" sẽ nằm trong bộ ban đầu. Điều này nghe có vẻ - và thực sự là - cực kỳ dễ viết mã. Rốt cuộc, chúng tôi đã cóSet<T>.removeAll
phải giúp chúng ta, phải không?Chúng tôi chỉ định kích thước của tập hợp "nguồn" và kích thước của tập hợp "loại bỏ" trên dòng lệnh và xây dựng cả hai. Tập hợp nguồn chỉ chứa các số nguyên không âm; tập hợp loại bỏ chỉ chứa các số nguyên âm. Chúng tôi đo lường thời gian cần thiết để loại bỏ tất cả các yếu tố bằng cách sử dụng
System.currentTimeMillis()
, đây không phải là đồng hồ bấm giờ chính xác nhất thế giới nhưng là quá đủ trong trường hợp này, như bạn sẽ thấy. Đây là mã: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"); } }
Hãy bắt đầu bằng cách tạo cho nó một công việc dễ dàng: một bộ nguồn gồm 100 mục và 100 mục để xóa:
c:UsersJonTest>java Test 100 100 Time taken: 1ms
Được rồi, vì vậy chúng tôi không mong đợi nó sẽ chậm… rõ ràng là chúng tôi có thể tăng cường mọi thứ lên một chút. Làm thế nào về nguồn một triệu mục và 300.000 mục cần loại bỏ?
c:UsersJonTest>java Test 1000000 300000 Time taken: 38ms
Hừ! Điều đó vẫn có vẻ khá nhanh. Bây giờ tôi cảm thấy mình hơi tàn nhẫn, yêu cầu nó làm tất cả những điều đó. Hãy làm cho nó dễ dàng hơn một chút - 300.000 mục nguồn và 300.000 mục xóa:
c:UsersJonTest>java Test 300000 300000 Time taken: 178131ms
Xin lỗi? Gần ba phút ? Rất tiếc! Chắc chắn sẽ dễ dàng hơn để xóa các mục khỏi bộ sưu tập nhỏ hơn bộ sưu tập mà chúng tôi quản lý trong 38 mili giây?
Ai đó có thể giải thích tại sao điều này đang xảy ra? Tại sao HashSet<T>.removeAll
phương pháp này quá chậm?