введение
Мы знаем, что для коллекций существует абстрактный метод removeAll(Collection> c)!
Но знаете ли вы, что в случае большого набора данныхArrayList.removeAll(Set)
скорость намного выше, чемArrayList.removeAll(List)
!
Я просто протестировал его и удалил 300 000 данных из 1 миллиона данных, первое занимает 0,031 секунды, а второе — 1267 секунд!
Это не паникёр, все желающие могут сходить и протестировать.
проводить исследования
анализ структуры класса
Сначала взгляните на приблизительную диаграмму структуры класса:
HashSet
,LinkedList
,ArrayList
),КромеArrayList
понял это самremoveAll()
Кроме метода две другие коллекции удаляются с помощью итератора Iterator родительского класса (или супер-родительского класса).
Может быть, поэтомуArrayList
изremoveAll()
Метод показывает «разные» причины для разных типов параметров~!
жевать код
Рассмотрим подробнее класс ArrayList.removeAll()
реализация метода.
В целях экономии времени на просмотр официалов я не буду выкладывать конкретный код, а выложу псевдокод, который легче читать:
如:list.removeAll(subList);
//1.将list中不删除的元素移到数组前面(我们知道ArrayList的底层是数组实现)
int w=0; //w为不删除和要删除的分界线
for(var value in 该list的底层数组){
if(!subList.contain(value)){
该list的底层数组[w]=value;
w++;
}
}
//2.将w后面的元素全部置为null
xxx
Среди них мы можем увидеть ключевой шаг, влияющий на курс:subList.contain(value)
!
Так что разница в скорости на самом деле заключается в参数集合.contain()
разница в методе
HashSet.contains() vs ArrayList.contains()
- ArrayList.contains()
Реализация очень проста, то есть вызов indexOf() и обход поиска один за другим. Худшая временная сложность
O(总数据量)
.
- HashSet.contains()
Мы знаем, что нижний уровень HashSet — это HashMap, поэтому фактический вызов — это вызов
map.containKey()
метод.
Как мы все знаем, скорость поиска HashMap очень высока!
Поэтому здесь мы также объясняем проблему заголовка. При этом я также знаю, что в случае относительно большого объема данных использоватьarrayList.removeAll(subList)
можно изменить на:
- будет
subList
упаковано какHashSet
:arrayList.removeAll(new HashSet(subList))
- будет
arrayList
изменить наLinkedList
:new LinkedList(arrayList).removeAll(subList)
Давайте снова поговорим о HashMap.containKey()
Сказав это, невозможно немного рассказать о картах.
Первая картинка:
Мы знаем, что нижний слой HashMap数组
+链表
. иcontainsKey()
На самом деле дноgetEntry(key)
, а затем оцените, является ли запись нулевой для достижения цели!
В JDK1.8,getEntry()
которыйgetNode()
. Кроме того,get(key)
Нижний слой метода также(e=getEntry(key))!=null?e.value:null
.
Хватит болтать, вернемся к делу.
На графике верхняя строка представляет собой массив, а каждый столбец — связанный список.
Где должен быть размещен каждый элемент, возможно, потребуются следующие шаги:
- Определите, в каком индексе массива находится ключ:
索引位置 = (数组.size-1) & hash(key.hashcode())
- В предыдущей версии использовалась описанная выше битовая операция.
&
заменяется излишком%
, эффект тот же, все для предотвращения превышения значения хэш-кода длины массива. Однако эффективность битовой операции определенно выше, чем у остальных.
- Популярная наука:
a & b = c
,Такc<=min(a,b)
, поэтому результирующий индекс всегда меньше, чем数组.size-1
, почему он меньше или равенc<=min(a,b)
?
- Например: 4 и 8 = 00000100 и 00001000, та же позиция
与运算
,与运算
Верно и то, и другое! Итак, смотрим на наименьшее число (00000100
), любое число, выполненное с ним与运算
, первые 5 цифр не могут быть 1, поэтому результат может быть меньше или равен 4~
- Также обратите внимание, что метод hash() используется выше, чтобы сохранить единообразие хеша всех ключей Зачем вы хотите это сделать?
- Например, если вы переопределяете метод hashcode, он возвращает 1. Наконец, когда хэш-карта сохраняет такие объекты, все они помещаются в одну и ту же позицию индекса!
- Для входа с Entry.next=null он становится Entry.next=new Node()
- Примечание. Если данные слишком велики, JDK1.8 автоматически переключит связанный список на реализацию красно-черного дерева.
Поэтому простоcontainsKey()
Худшая временная сложность:O((总数据量/数组长度)*最长链表长度)
и это数组长度
Как долго это? Какова длина связанного списка? Как они соотносятся с объемом данных?
Нам нужно кратко изучить реализацию HashMap:
На рисунке показано,数组长度
Как правило, это больше, чем общий объем данных (когда коэффициент загрузки
Итак, какова длина связанного списка?
Представить,数组长度
>= общий объем данных, то в лучшем случае (хэш каждого данных равномерно распределен) в связанном списке может быть один элемент, то есть временная сложность может быть O(1)!
По крайней мере, в большинстве случаев длина связанного списка не будет слишком большой, если только вы не перепишете хэш-код, чтобы он всегда возвращал 1!