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 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 .