ArrayList удалить против удалить все

Что лучше использовать, если я хочу удалить коллекцию из массива? Я думаю, что метод removeAll в ArrayList написан для этой задачи, но в тесте, который я написал, простое перебор объектов и удаление их по отдельности было на несколько секунд быстрее.

Что вы используете для этой цели?

редактировать:

код removeAll, который я нашел в grepcode, вызывает batchRemove (c, false):

private boolean More ...batchRemove (коллекция c, логическое дополнение) {

700         final Object[] elementData = this.elementData;
701         int r = 0, w = 0;
702         boolean modified = false;
703         try {
704             for (; r < size; r++)
705                 if (c.contains(elementData[r]) == complement)
706                     elementData[w++] = elementData[r];
707         } finally {
708             // Preserve behavioral compatibility with AbstractCollection,
709             // even if c.contains() throws.
710             if (r != size) {
711                 System.arraycopy(elementData, r,
712                                  elementData, w,
713                                  size - r);
714                 w += size - r;
715             }
716             if (w != size) {
717                 // clear to let GC do its work
718                 for (int i = w; i < size; i++)
719                     elementData[i] = null;
720                 modCount += size - w;
721                 size = w;
722                 modified = true;
723             }
724         }
725         return modified;
726     }

я на самом деле не понимаю..

мой тестовый код был таким:

public class RemoveVsRemovall {

    public static void main(String[] args){
        ArrayList<String> source = new ArrayList<>();
        ArrayList<String> toRemove = new ArrayList<>();
        for(int i = 0; i < 30000; i++){
            String s = String.valueOf(System.nanoTime());
            source.add(s);
            if(i % 2 == 0) toRemove.add(s);
        }
        long startTime = System.nanoTime();
        removeList1(source, toRemove);
        long endTime = System.nanoTime();
        System.out.println("diff: " + (endTime - startTime) * 1e-9);
    }

    static void removeList1(ArrayList<String> source, ArrayList<String> toRemove){
        source.removeAll(toRemove);
    }

    static void removeList2(ArrayList<String> source, ArrayList<String> toRemove){
        for(String s : toRemove){
            source.remove(s);
        }
    }
}

вызывая его несколько раз с разными размерами списка и переключаясь между двумя методами.


person T_01    schedule 01.03.2015    source источник
comment
Я полагаю, что в вашем тесте была ошибка. Покажите нам свой тестовый код. (Мне трудно поверить, что есть действительно существенная разница в производительности. И писать тесты производительности, дающие точные результаты, довольно сложно.)   -  person Stephen C    schedule 01.03.2015
comment
Почему бы вам не изучить код методов remove и removeAll? Тем не менее, этот вопрос не заслуживает отрицательного голоса. +1 от меня. На SO есть вопросы похуже с 200 + плюсами, чем этот.   -  person CKing    schedule 01.03.2015
comment
@bot, а можно поинтересоваться, где прогресс?   -  person Gabe    schedule 01.03.2015
comment
@ Гейб, я не понимаю, что ты имеешь в виду.   -  person CKing    schedule 01.03.2015
comment
В этом вопросе несколько обсуждается removeAll. Сложность O (n ^ 2). Возможно, использование Iterator делает его немного медленнее, но не уверен, что вы можете сделать действительно лучше.   -  person Cyril Duchon-Doris    schedule 01.03.2015
comment
@bot Я считаю несправедливым продвигать вопрос, основываясь на том факте, что посты явно хуже, но имеют значительно лучший рейтинг; разные времена, разные масштабы - несправедливое суждение imo. Опять же, это только одна сторона зрения.   -  person Gabe    schedule 01.03.2015
comment
@Гейб Достаточно честно. Даже если мы не сравниваем этот вопрос с другими вопросами, немного терпимости перед отрицательным голосованием не так уж много. За этот вопрос проголосовали, как только он был опубликован.   -  person CKing    schedule 01.03.2015
comment
Как я и подозревал, код бенчмарка ошибочен. Вы не прогреваете JVM должным образом, и это, вероятно, больше влияет на случай removeAll, чем на случай remove.   -  person Stephen C    schedule 01.03.2015
comment
@StephenC, не могли бы вы объяснить, что означает разогрев JVM? Вы имеете в виду JIT?   -  person Kishore    schedule 01.03.2015
comment
@Kishore - прочитайте это: stackoverflow.com/questions/504103/   -  person Stephen C    schedule 01.03.2015


Ответы (2)


Есть несколько причин, по которым трудно дать общий ответ на этот вопрос.

Во-первых, вы должны понимать, что эти характеристики производительности зависят от реализации. Вполне возможно, что реализация зависит от платформы и версии JDK.

Сказав это, в основном есть 2 стратегии реализации removeAll:

  1. Для каждого элемента вашего ArrayList проверьте, есть ли он в другом Collection; если да то удали.
  2. Для каждого элемента Collection проверьте, находится ли он в ArrayList; если да то удали.

Если Collection выполняет содержит в постоянное время, стратегия 1 (асимптотически) выигрывает. С другой стороны, если contains выполняется путем сканирования всего соединения, а Collection выполняется очень медленно, стратегия 2 обычно имеет преимущество, поскольку она выполняет итерацию Collection только один раз; но даже в этом случае, если Collection очень велико и большинство элементов ArrayList находятся среди первых элементов Collection, стратегия 1 снова выигрывает... этому нет конца.

Вероятно, вам лучше довериться реализации removeAll(); если это не удается, попробуйте изменить структуры данных; и если это тоже не удается, реализуйте свой собственный метод на основе эмпирических тестов.

person Valentin Waeselynck    schedule 01.03.2015

Еще одна вещь, которую следует учитывать:

Код Java проверен веками и написан таким образом, чтобы адаптироваться к множеству различных и особых случаев (см. комментарий Preserve behavioral compatibility with AbstractCollection).

Итак, на самом деле, вы можете написать свою собственную реализацию методов, которая будет работать быстрее. Но, с другой стороны, уверены ли вы, что сможете справиться со всеми особыми случаями, с которыми столкнулись разработчики Java с момента рождения Java?

Также примите во внимание, что некоторые функции Java могут использовать некоторую реализацию C для ускорения работы. Здесь, видимо, не так, но могло бы.

person Cyril Duchon-Doris    schedule 01.03.2015
comment
так что вы рекомендуете использовать removeAll ? - person T_01; 01.03.2015
comment
Если вы действительно не заботитесь об оптимальной производительности и знаете, что вам нужно иметь дело только с определенными наборами данных, это будет вести себя аналогично вашему тестовому коду (черт возьми, я даже не уверен, что это можно легко проверить), да. - person Cyril Duchon-Doris; 01.03.2015