Какова временная и пространственная сложность метода retainAll при использовании на HashSets в Java?
например, в приведенном ниже коде:
public int commonTwo(String[] a, String[] b)
{
Set common = new HashSet<String>(Arrays.asList(a));
common.retainAll(new HashSet<String>(Arrays.asList(b)));
return common.size();
}
2 ответов
давайте внимательно рассмотрим код. Метод retainAll
- это наследство от AbstractCollection
и (по крайней мере, в OpenJDK) выглядит так:
public boolean retainAll(Collection<?> c) {
boolean modified = false;
Iterator<E> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
есть один большой это отметить здесь, мы петля над this.iterator()
и звонок c.contains
. Таким образом, сложность времени n
звонки c.contains
здесь n = this.size()
и не более n
звонки it.remove()
.
это важно, что contains
метод вызывается на другое Collection
и так сложность зависит от сложности другого Collection
contains
.
Итак, в то время как:
Set<String> common = new HashSet<>(Arrays.asList(a));
common.retainAll(new HashSet<>(Arrays.asList(b)));
будет O(a.length)
, as HashSet.contains
и HashSet.remove
как O(1)
(амортизируется).
если вы
common.retainAll(Arrays.asList(b));
тогда из-за O(n)
contains
on Arrays.ArrayList
это стало бы O(a.length * b.length)
- т. е. расходы O(n)
копирование массива в HashSet
вы на самом деле принять вызов retainAll
гораздо быстрее.
As что касается сложности пространства, то нет дополнительного пространства (за пределами Iterator
) требуется retainAll
, но ваш вызов на самом деле довольно дорогой космический, поскольку вы выделяете два новых HashSet
реализаций, которые на самом деле являются полноценными HashMap
.
можно отметить еще две вещи:
- нет причин выделять
HashSet
с элементамиa
- более дешевая коллекция, которая также имеетO(1)
удалить из середины, такие какLinkedList
можете использовать. (дешевле в памяти, а также время сборки - хэш-таблица не строится) - ваши изменения теряются при создании новых экземпляров коллекции и возвращаются только
b.size()
.
реализация может быть найдена в java.util.AbstractCollection
класса. Способ его реализации выглядит следующим образом:
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<E> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
таким образом, он будет повторять все в вашем common
установите и проверьте, содержит ли этот элемент коллекция, переданная в качестве параметра.
в вашем случае оба HashSet
s, таким образом, это будет O(n), так как содержит должно быть O(1) амортизируется и повторяется над вашим common
set - O (n).
одно улучшение, которое вы можете сделать, просто не копировать a
в новый HashSet
, потому что он будет использоваться в любом случае вы можете сохранить список.