Джон Скіт нещодавно підняв цікаву тему програмування у своєму блозі: "У моїй абстракції є дірка, дорога Ліза, дорога Ліза" (курсив додано):
У мене є набір -
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
Перепрошую? Майже три хвилини ? Так! Напевно, було б простіше видалити предмети з меншої колекції, ніж та, що нам вдалося за 38 мс?
Хтось може пояснити, чому це відбувається? Чому HashSet<T>.removeAll
метод так повільний?