Есть ли какие-либо результаты тестирования производительности при сравнении традиционного цикла for и Iterator при просмотре ArrayList, HashMap и других коллекций?
Или просто зачем мне использовать Iterator для цикла или наоборот?
Есть ли какие-либо результаты тестирования производительности при сравнении традиционного цикла for и Iterator при просмотре ArrayList, HashMap и других коллекций?
Или просто зачем мне использовать Iterator для цикла или наоборот?
Предполагая, что это то, что вы имели в виду:
// traditional for loop
for (int i = 0; i < collection.size(); i++) {
T obj = collection.get(i);
// snip
}
// using iterator
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
T obj = iter.next();
// snip
}
// using iterator internally (confirm it yourself using javap -c)
for (T obj : collection) {
// snip
}
Итератор работает быстрее для коллекций без произвольного доступа (например, TreeSet, HashMap, LinkedList). Для массивов и ArrayLists разница в производительности должна быть незначительной.
Изменить: я считаю, что микротестирование - это корень зла, как и ранняя оптимизация. Но опять же, я думаю, что хорошо иметь представление о последствиях таких довольно тривиальных вещей. Поэтому я провел небольшой тест:
Результаты аналогичны для всех, кроме «для со счетчиком» с LinkedList. Всем остальным пяти потребовалось менее 20 миллисекунд, чтобы перебрать весь список. Использование list.get(i) в LinkedList 100 000 раз заняло более 2 минут (!) (В 60 000 раз медленнее). Вау! :) Следовательно, лучше использовать итератор (явно или неявно для каждого), особенно если вы не знаете, с каким типом и размером списка вы имеете дело.
Iterator<T> iter = collection.iterator(); int l = collection.size(); for (int i = 0, i < l; i++) { T obj = iter.next(); // snip }
- person JustAnotherCoder; 20.01.2021
hasNext() не должна быть сложной операцией независимо от фактической реализации. Однако это, безусловно, следует рассматривать как плохое использование API.
- person sfussenegger; 20.01.2021
Первая причина использовать итератор - это очевидная правильность. Если вы используете ручной индекс, могут быть очень безобидные отдельные ошибки, которые вы сможете увидеть, только если внимательно присмотритесь: вы начали с 1 или с 0? Вы закончили на length - 1? Вы использовали < или <=? Если вы используете итератор, гораздо легче увидеть, что он действительно выполняет итерацию всего массива. «Говори, что делаешь, делай, что говоришь».
Вторая причина - единообразный доступ к разным структурам данных. К массиву можно получить эффективный доступ через индекс, но связанный список лучше всего просматривать, запоминая последний доступный элемент (в противном случае вы получите "Шлемиэль художник "). Хэш-карта еще сложнее. Предоставляя единообразный интерфейс для этих и других структур данных (например, вы также можете выполнять обход дерева), вы снова получаете очевидную корректность. Логика обхода должна быть реализована только один раз, и код, использующий ее, может кратко «сказать, что он делает, и сделать то, что он говорит».
Производительность в большинстве случаев одинаковая.
Однако всякий раз, когда код получает список и зацикливается на нем, есть хорошо известный случай:
Итератор лучше подходит для всех реализаций списков, которые не реализуют RandomAccess (пример: LinkedList) .
Причина в том, что для этих списков доступ к элементу по индексу не является операцией с постоянным временем.
Таким образом, вы также можете рассматривать Iterator как более надежный (в отношении деталей реализации).
Как всегда, производительность не должна скрывать проблемы с удобочитаемостью.
Цикл java5 foreach является большим хитом в этом аспекте :-)
Да, это имеет значение для коллекций, которые не имеют произвольного доступа, как LinkedList. Связанный список внутренне реализуется узлами, указывающими на следующий (начиная с головного узла).
Метод get (i) в связанном списке начинается с головного узла и перемещается по ссылкам до i-го узла. Когда вы выполняете итерацию по связанному списку с использованием традиционного цикла for, вы каждый раз начинаете заново с головного узла, таким образом, общий обход становится квадратичным по времени.
for( int i = 0; i< list.size(); i++ ) {
list.get(i); //this starts everytime from the head node instead of previous node
}
В то время как for каждый цикл перебирает итератор, полученный из связанного списка, и вызывает его метод next (). Итератор поддерживает состояния последнего доступа и, таким образом, не запускается каждый раз полностью с головы.
for( Object item: list ) {
//item element is obtained from the iterator's next method.
}
Одна из лучших причин использовать итератор поверх синтаксиса i ++ заключается в том, что не все структуры данных будут поддерживать произвольный доступ, не говоря уже о том, чтобы он работал хорошо. Вы также должны программировать интерфейс списка или коллекции, чтобы, если вы позже решили, что другая структура данных будет более эффективной, вы могли бы заменить ее без масштабной операции. В этом случае (в случае кодирования интерфейса) вам не обязательно знать детали реализации, и, вероятно, будет разумнее отложить это до самой структуры данных.
Одна из причин, по которой я научился придерживаться for each, заключается в том, что он упрощает вложенные циклы, особенно более 2-х мерных циклов. Все «i», «j» и «k», которыми вы можете в конечном итоге манипулировать, могут очень быстро запутаться.
Используйте JAD или JD-GUI против вашего сгенерированного кода, и вы увидите, что реальной разницы нет. Преимущество новой формы итератора в том, что она выглядит чище в вашей кодовой базе.
Изменить: из других ответов я вижу, что вы действительно имели в виду разницу между использованием get (i) и итератором. Я понял, что исходный вопрос означает разницу между старым и новым способами использования итератора.
Использование get (i) и поддержание собственного счетчика, особенно для классов List, не является хорошей идеей по причинам, указанным в принятом ответе.
Я не верю в это
for (T obj : collection) {
вычисляет .size () каждый раз в цикле и, следовательно, быстрее, чем
for (int i = 0; i < collection.size(); i++) {
for (int i = 0, l = collection.size(); i < l; i++) {
- person Adrian Bartholomew; 03.07.2013
+1 к тому, что сказал сфуссенеггер. К вашему сведению, используете ли вы явный итератор или неявный (то есть для каждого), это не повлияет на производительность, потому что они компилируются с одним и тем же байтовым кодом.
get(i)повторяется из заголовка спискаiраз. Я уверен, что это интуитивно очевидно для всех присутствующих, но мне потребовалась минута, чтобы понять, почему. - person Frank Thomas   schedule 15.01.2015