Лучший способ удалить один элемент arraylist из другого arraylist
каков наилучший метод производительности в Java (7,8) для устранения integer
элементы Arraylist
из другого. Все элементы уникальны в первом и втором списках.
на данный момент я знаю метод API removeall
и использовать его таким образом:
tempList.removeAll(tempList2);
проблема возникает, когда я работаю с arraylists имеют более 10000 элементов. Например, когда я удаляю 65000 элементов, задержка составляет около 2 секунд. Но мне нужно бороться с еще большим. большие списки с более чем 1000000 элементов.
какова стратегия в этом вопросе?
может быть, что-то с новым API потока должно решить его?
3 ответов
tl; dr:
сохранить его простым. Использовать
list.removeAll(new HashSet<T>(listOfElementsToRemove));
.
как уже упоминал Эран в ответ: низкая производительность связана с тем, что псевдокод родовой removeAll
реализация
public boolean removeAll(Collection<?> c) {
for (each element e of this) {
if (c.contains(e)) {
this.remove(e);
}
}
}
так contains
вызов, который выполняется в списке элементов для удаления, вызовет производительность O(n*k) (где n
- количество элементов в уберите, и k
- это количество элементов в списке, на которое вызывается метод).
наивно можно было представить, что this.remove(e)
вызов List
может также иметь O (k), и эта реализация также будет иметь квадратичную сложность. Но это не так: Вы упомянули, что списки специально ArrayList
экземпляров. И ArrayList#removeAll
метод реализован для делегирования метода с именем batchRemove
это напрямую работает с базовым массивом и делает не удалить элементы по отдельности.
поэтому все, что вам нужно сделать, это убедиться, что поиск в коллекции, содержащей элементы для удаления, быстрый - предпочтительно O(1). Этого можно достичь, поместив эти элементы в Set
. В конце концов, его можно просто записать как
list.removeAll(new HashSet<T>(listOfElementsToRemove));
побочные Примечания:
ответ Eran имеет IMHO два основных недостатка: прежде всего, он требует сортировка списки, Это O (N * logn) - и это просто не обязательно. Но что более важно (и очевидно):сортировка, скорее всего, изменит порядок элементов! что, если это просто не нужные?
удаленно связанные: есть некоторые другие тонкости, связанные с removeAll
реализаций. Например, метод HashSet removeAll удивительно медленный в некоторых случаях. Хотя это также сводится к O (n*n), когда удаляемые элементы хранятся в a лист, точное поведение действительно может быть удивительным в этом конкретном случае.
Ну так removeAll
проверяет каждый элемент tempList
появляется ли он в tempList2
, время работы пропорционально размеру первого списка, умноженному на размер второго списка, что означает O(N^2)
если один из двух списков очень мала и может рассматриваться как "постоянный размер".
если, с другой стороны, вы предварительно отсортируете списки, а затем выполните итерацию по обоим спискам с одной итерацией (аналогично шагу слияния в сортировке слияния), сортировка займет O(NlogN)
и итерационный O(N)
, давая вам общее время работы O(NlogN)
. Вот!--8--> - размер большего из двух списков.
если вы можете заменить списки отсортированной структурой (возможно,TreeSet
, так как вы сказали, что элементы уникальны), вы можете реализовать removeAll
в линейном времени, так как вам не придется выполнять сортировку.
Я не тестировал его, но что-то вроде этого может работать (при условии, что оба tempList
и tempList2
сортируются) :
Iterator<Integer> iter1 = tempList.iterator();
Iterator<Integer> iter2 = tempList2.iterator();
Integer current = null;
Integer current2 = null;
boolean advance = true;
while (iter1.hasNext() && iter2.hasNext()) {
if (advance) {
current = iter1.next();
advance = false;
}
if (current2 == null || current > current2) {
current2 = iter2.next();
}
if (current <= current2) {
advance = true;
if (current == current2)
iter1.remove();
}
}
Я подозреваю, что удаление из ArrayList, является хитом perfromance, так как список может быть разделен, когда элемент в середине удаляется, или если список должен быть уплотнен после удаления элемента. Это может быть быстрее сделать:
- создать "набор" элементов для удаления
- создайте новый результат ArrayList, который вам нужен, назовите его R. Вы можете дать ему достаточный размер при построении.
- повторите исходный список, из которого вам нужны элементы он удален, если элемент найден в наборе, не добавляйте его в R, иначе добавьте его.
Это надо O(N)
; при создании набора и поиска в нем считается постоянным.