Лучший способ удалить один элемент 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, так как список может быть разделен, когда элемент в середине удаляется, или если список должен быть уплотнен после удаления элемента. Это может быть быстрее сделать:

  1. создать "набор" элементов для удаления
  2. создайте новый результат ArrayList, который вам нужен, назовите его R. Вы можете дать ему достаточный размер при построении.
  3. повторите исходный список, из которого вам нужны элементы он удален, если элемент найден в наборе, не добавляйте его в R, иначе добавьте его.

Это надо O(N); при создании набора и поиска в нем считается постоянным.