Рубрики
Без рубрики

Производительность функции удалить все() в наборе хэшей

Изучите производительность удаления() в HashSet.

Автор оригинала: Amit Bhave.

1. Обзор

HashSet-это коллекция для хранения уникальных элементов.

В этом уроке мы обсудим производительность метода removal() в java.util.Хэш-набор класс.

2. Набор хэшей.удалите все()

Метод removeAll удаляет все элементы, содержащиеся в коллекции :

Set set = 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 Set employeeSet3 = 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 .