Джон Скит недавно поднял интересную тему программирования в своем блоге: «В моей абстракции есть дыра, дорогая Лиза, дорогая Лиза» (курсив мой ):
У меня есть набор - собственно
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
метод такой медленный?