1. Обзор
HashSet-это коллекция для хранения уникальных элементов.
В этом уроке мы обсудим производительность метода removal() в java.util.Хэш-набор класс.
2. Набор хэшей.удалите все()
Метод removeAll удаляет все элементы, содержащиеся в коллекции :
Setset = new HashSet (); set.add(1); set.add(2); set.add(3); set.add(4); Collection collection = new ArrayList (); collection.add(1); collection.add(3); set.removeAll(collection); Integer[] actualElements = new Integer[set.size()]; Integer[] expectedElements = new Integer[] { 2, 4 }; assertArrayEquals(expectedElements, set.toArray(actualElements));
В результате элементы 1 и 3 будут удалены из набора.
3. Внутренняя реализация и временная сложность
Метод removeAll() определяет, какой из них меньше – набор или коллекция. Это делается путем вызова метода size() для набора и коллекции.
Если коллекция содержит меньше элементов , чем набор , то она выполняет итерацию по указанной коллекции с временной сложностью O( n ). Он также проверяет, присутствует ли элемент в наборе с временной сложностью O(1). И если элемент присутствует, он удаляется из набора с помощью метода remove() набора, который снова имеет временную сложность O(1). Таким образом общая временная сложность равна O( n ) .
Если набор содержит меньше элементов , чем коллекция , то он повторяет этот набор, используя O( n ). Затем он проверяет, присутствует ли каждый элемент в коллекции, вызывая его метод contains () . И если такой элемент присутствует, то элемент удаляется из набора. Таким образом, это зависит от временной сложности метода contains () .
Теперь в этом случае, если коллекция является ArrayList , временная сложность метода contains() равна O( m ). Таким образом общая трудоемкость удаления всех элементов, присутствующих в списке массивов из набора, составляет O( n * m ) .
Если коллекция снова HashSet , временная сложность метода contains() равна O(1). Таким образом общая трудоемкость удаления всех элементов, присутствующих в хэш-наборе из набора, составляет O( n ) .
4. Производительность
Чтобы увидеть разницу в производительности между вышеперечисленными 3 случаями, давайте напишем простой тест JMH benchmark.
В первом случае мы инициализируем набор и коллекцию, где в наборе больше элементов, чем в коллекции. Во втором случае мы инициализируем набор и коллекцию, где у нас больше элементов в коллекции, чем в наборе. И в третьем случае мы инициализируем 2 набора, где у нас будет 2-й набор, содержащий больше элементов, чем 1-й:
@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5) public class HashSetBenchmark { @State(Scope.Thread) public static class MyState { private Set employeeSet1 = new HashSet<>(); private List employeeList1 = new ArrayList<>(); private Set employeeSet2 = new HashSet<>(); private List employeeList2 = new ArrayList<>(); private SetemployeeSet3 = new HashSet<>(); private Set employeeSet4 = new HashSet<>(); private long set1Size = 60000; private long list1Size = 50000; private long set2Size = 50000; private long list2Size = 60000; private long set3Size = 50000; private long set4Size = 60000; @Setup(Level.Trial) public void setUp() { // populating sets } } }
После этого мы добавим наши контрольные тесты:
@Benchmark public boolean given_SizeOfHashsetGreaterThanSizeOfCollection_whenRemoveAllFromHashSet_thenGoodPerformance(MyState state) { return state.employeeSet1.removeAll(state.employeeList1); } @Benchmark public boolean given_SizeOfHashsetSmallerThanSizeOfCollection_whenRemoveAllFromHashSet_thenBadPerformance(MyState state) { return state.employeeSet2.removeAll(state.employeeList2); } @Benchmark public boolean given_SizeOfHashsetSmallerThanSizeOfAnotherHashSet_whenRemoveAllFromHashSet_thenGoodPerformance(MyState state) { return state.employeeSet3.removeAll(state.employeeSet4); }
И вот результаты:
Benchmark Mode Cnt Score Error Units HashSetBenchmark.testHashSetSizeGreaterThanCollection avgt 20 2700457.099 ± 475673.379 ns/op HashSetBenchmark.testHashSetSmallerThanCollection avgt 20 31522676649.950 ± 3556834894.168 ns/op HashSetBenchmark.testHashSetSmallerThanOtherHashset avgt 20 2672757.784 ± 224505.866 ns/op
Мы можем видеть, как Набор хэшей.удалить все() работает довольно плохо, когда Набор хэшей имеет меньше элементов, чем Коллекция , который передается в качестве аргумента в удаление() метод. Но когда другая коллекция снова Набор хэшей , тогда производительность хорошая.
5. Заключение
В этой статье мы рассмотрели производительность remove All() в HashSet. Когда набор содержит меньше элементов, чем коллекция, производительность remove All() зависит от временной сложности метода contains() коллекции.
Как обычно, полный код этой статьи доступен на GitHub .