за-против против за время - PullRequest
6 голосов
/ 05 декабря 2010

Интересно, как лучше всего реализовать цикл for-each для ArrayList или любого вида List.

Какая из следующих реализаций является лучшей и почему? Или есть лучший способ?

Спасибо за вашу помощь.

<code>
List values = new ArrayList();</p>

<p>values.add("one");
values.add("two");
values.add("three");
...</p>

<p>//#0<br>
for(String value : values) {
    ...
}</p>

<p>//#1<br>
for(int i = 0; i < values.size(); i++) {
    String value = values.get(i);
    ...
}</p>

<p>//#2<br>
for(Iterator it = values.iterator(); it.hasNext(); ) {
    String value = it.next();
    ...
}</p>

<p>//#3<br>
Iterator it = values.iterator();
while (it.hasNext()) {
    String value = (String) it.next();
    ...
}

Ответы [ 4 ]

20 голосов
/ 05 декабря 2010

# 3 имеет недостаток, поскольку область действия итератора it выходит за пределы конца цикла. Другие решения не имеют этой проблемы.

# 2 в точности совпадает с # 0, за исключением того, что # 0 более читабелен и менее подвержен ошибкам.

# 1 (вероятно) менее эффективен, потому что он вызывает .size() каждый раз через цикл.

# 0 обычно лучше, потому что:

  • это самый короткий
  • наименее подвержен ошибкам
  • идиоматично и легко читать с первого взгляда
  • эффективно реализован компилятором
  • не загрязняет область вашего метода (вне цикла) ненужными именами
2 голосов
/ 05 декабря 2010

Короткий ответ - использовать версию 0. Взгляните на заголовок раздела Использовать расширенный синтаксис цикла на Документация Android для Designing for Performance .На этой странице есть куча вкусностей, она очень четкая и лаконичная.

1 голос
/ 05 декабря 2010

В ответ на этот комментарий от OP.

Однако # 1 требуется при обновлении (если не просто изменение текущего элемента или построение результатов в виде нового списка) и поставляется синдекс.Поскольку в этом случае List <> является ArrayList <>, get () (и size ()) равны O (1), но это не одно и то же для всех типов контрактов List.

Давайте рассмотрим следующие проблемы:

Конечно, get(int) не является O(1) для всех реализаций List контракта.Однако AFAIK size() равен O(1) для всех List реализаций в java.util.Но вы правы, что # 1 неоптимален для многих реализаций List.Действительно, для таких списков, как LinkedList, где get(int) равно O(N), подход # 1 приводит к итерации списка O(N^2).

В случае ArrayList, это просто сделать вручную hoist вызов size(), присвоение его (окончательной) локальной переменной.С этой оптимизацией код # 1 значительно быстрее, чем в других случаях ... для ArrayList s.

Ваша точка зрения об изменении списка при итерации элементов вызывает ряд проблем:

  • Если вы делаете это с помощью решения, которое явно или неявно использует итераторы, то в зависимости от класса списка вы можете получить ConcurrentModificationException с.Если вы используете один из классов одновременной коллекции, вы не получите исключение, но javadocs заявляет, что итератор не обязательно вернет все элементы списка.

  • Если высделайте это, используя код # 1 (как есть), тогда у вас есть проблема.Если модификация выполняется одним и тем же потоком, вам нужно настроить индексную переменную, чтобы избежать пропущенных записей или вернуть их дважды.Даже если вы все сделаете правильно, запись списка, одновременно вставленная до текущей позиции, не будет отображаться.

  • Если изменение в случае # 1 выполняется другим потоком, онотрудно правильно синхронизировать.Основная проблема заключается в том, что get(int) и size() являются отдельными операциями.Даже если они синхронизируются по отдельности, ничто не мешает другому потоку изменить список между вызовами size и get.

Короче говоря, итерация списка, которыйБыть модифицированным одновременно сложно, и его следует избегать ... , если вы действительно не знаете, что делаете .

1 голос
/ 05 декабря 2010

# 0, на мой взгляд, легче всего читать, но # 2 и # 3 будут работать так же хорошо. Между этими тремя не должно быть никакой разницы в производительности.

Практически ни при каких обстоятельствах вы не должны использовать # 1. Вы заявляете в своем вопросе, что вы можете перебирать «каждый вид списка». Если вам случится итерация по LinkedList, тогда # 1 будет n ^ 2 сложность: не хорошо. Даже если вы абсолютно уверены в том, что используете список, поддерживающий эффективный произвольный доступ (например, ArrayList), обычно нет причин использовать № 1 среди других.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...