Foreach() — Зачем в коллекциях Java метод forEach()


Содержание

Изменение элементов массмва с foreach

28.09.2014, 13:26

Сортировка массмва структур
Возник вопрос можно ли организовать как нибудь сортировку массива структур, по какому либо эл-ту из.

Изменение значения в foreach
Как? Пробовал как в PHP, но в шарпе жалуется студия. int chisla = < 1, 1, 1, 1, 1 >; .

Цикл foreach изменение Label
Вообще завис крч я, не знаю что делать, или уже мозги просто не соображают от целого дня.

Parallel.Foreach изменение коллекции
Если коллекция Dictionary dic; Первый поток добавляет и удаляет элементы из.

Изменение переменной цикла foreach
Здравствуйте. Возник вопрос: к примеру у меня есть массив (большой размерности). Мне надо каждый.

Массив: перебирающие методы

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/array-methods.

Современный стандарт JavaScript предоставляет много методов для «умного» перебора массивов, которые есть в современных браузерах…

…Ну а для их поддержки в IE8- просто подключите библиотеку ES5-shim.

forEach

Метод «arr.forEach(callback[, thisArg])» используется для перебора массива.

Он для каждого элемента массива вызывает функцию callback .

Этой функции он передаёт три параметра callback(item, i, arr) :

  • item – очередной элемент массива.
  • i – его номер.
  • arr – массив, который перебирается.

Второй, необязательный аргумент forEach позволяет указать контекст this для callback . Мы обсудим его в деталях чуть позже, сейчас он нам не важен.

Метод forEach ничего не возвращает, его используют только для перебора, как более «элегантный» вариант, чем обычный цикл for .

filter

Метод «arr.filter(callback[, thisArg])» используется для фильтрации массива через функцию.

Он создаёт новый массив, в который войдут только те элементы arr , для которых вызов callback(item, i, arr) возвратит true .

Метод «arr.map(callback[, thisArg])» используется для трансформации массива.

Он создаёт новый массив, который будет состоять из результатов вызова callback(item, i, arr) для каждого элемента arr .

every/some

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

  • Метод «arr.every(callback[, thisArg])» возвращает true , если вызов callback вернёт true для каждого элемента arr .
  • Метод «arr.some(callback[, thisArg])» возвращает true , если вызов callback вернёт true для какого-нибудь элемента arr .

reduce/reduceRight

Метод «arr.reduce(callback[, initialValue])» используется для последовательной обработки каждого элемента массива с сохранением промежуточного результата.

Это один из самых сложных методов для работы с массивами. Но его стоит освоить, потому что временами с его помощью можно в несколько строк решить задачу, которая иначе потребовала бы в разы больше места и времени.

Метод reduce используется для вычисления на основе массива какого-либо единого значения, иначе говорят «для свёртки массива». Чуть далее мы разберём пример для вычисления суммы.

Он применяет функцию callback по очереди к каждому элементу массива слева направо, сохраняя при этом промежуточный результат.

Аргументы функции callback(previousValue, currentItem, index, arr) :

  • previousValue – последний результат вызова функции, он же «промежуточный результат».
  • currentItem – текущий элемент массива, элементы перебираются по очереди слева-направо.
  • index – номер текущего элемента.
  • arr – обрабатываемый массив.

Кроме callback , методу можно передать «начальное значение» – аргумент initialValue . Если он есть, то на первом вызове значение previousValue будет равно initialValue , а если у reduce нет второго аргумента, то оно равно первому элементу массива, а перебор начинается со второго.

Проще всего понять работу метода reduce на примере.

Например, в качестве «свёртки» мы хотим получить сумму всех элементов массива.

Вот решение в одну строку:

Разберём, что в нём происходит.

При первом запуске sum – исходное значение, с которого начинаются вычисления, равно нулю (второй аргумент reduce ).

Сначала анонимная функция вызывается с этим начальным значением и первым элементом массива, результат запоминается и передаётся в следующий вызов, уже со вторым аргументом массива, затем новое значение участвует в вычислениях с третьим аргументом и так далее.


Поток вычислений получается такой

В виде таблицы где каждая строка – вызов функции на очередном элементе массива:

sum current результат
первый вызов 1 1
второй вызов 1 2 3
третий вызов 3 3 6
четвёртый вызов 6 4 10
пятый вызов 10 5 15

Как видно, результат предыдущего вызова передаётся в первый аргумент следующего.

Кстати, полный набор аргументов функции для reduce включает в себя function(sum, current, i, array) , то есть номер текущего вызова i и весь массив arr , но здесь в них нет нужды.

Посмотрим, что будет, если не указать initialValue в вызове arr.reduce :

Результат – точно такой же! Это потому, что при отсутствии initialValue в качестве первого значения берётся первый элемент массива, а перебор стартует со второго.

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

Метод arr.reduceRight работает аналогично, но идёт по массиву справа-налево.

Итого

Мы рассмотрели методы:

  • forEach – для перебора массива.
  • filter – для фильтрации массива.
  • every/some – для проверки массива.
  • map – для трансформации массива в массив.
  • reduce/reduceRight – для прохода по массиву с вычислением значения.

Во многих ситуациях их использование позволяет написать код короче и понятнее, чем обычный перебор через for .

Перебирающие методы массивов изнутри

Методы массивов forEach , map , filter , some , every и reduce являются неотъемлимой частью функционального программирования на JavaScript. В этой статье подробно рассмотрим применение каждого из них, а также их реализацию в виде функций.

forEach

Самый простой способ разобраться, как работают функции высшего порядка, принимающие callback’и, — самостоятельно переписать несколько нативных методов. Начнём с самого простого метода Array.prototype.forEach . Метод массивов forEach принимает два аргумента: первый (обязательный) — callback функция, которая будет выполнена для каждого элемента массива один раз и второй (необязательный) — значение, которое будет использовано в качестве this при вызове функции callback . Работает это следующим образом:

В callback функцию передаётся три аргумента: item , соответствующий элементу массива, index , равный номеру элемента в массиве, и arr — ссылка на массив. Таким образом, справедливо следующее выражение arr[index] === item . Аргументам передаваемым в callback функцию можно давать разные названия в зависимости от контекста выполнения — выполнение callback функции от этого не изменится:

Как я писал выше, помимо callback функции метод forEach принимает ещё один аргумент — значение, которое будет использовано в качестве this при вызове функции callback . Зачем это может понадобиться? По умолчанию this не определён, то есть равен undefined . В некоторых ситуациях, особенно при работе с функциями конструкторами, необходимо задать контекст выполнения callback функции:

Подобный код, на первый взгляд, может оказаться полностью рабочим. Тем не менее, после его выполнения всё, что мы увидим будет ошибка Cannot read property ‘say’ of undefined . Данная ошибка показывает, что при выполнении метода forEach на передаваемом в метод mumble массиве phrases не задаётся контекст испольнения callback функции. Проще говоря, callback функция пытается выполнить подобный код undefined[say](words) . Решается данная проблема элементарно — передётся второй аргумент в метод forEach , который и указывает ему, что брать за this :

forEach своими руками

Теперь, когда вы знаете, как работает forEach , настало время написать функцию, которая делает всё тоже самое. При создании функции each будем руководствоваться тремя правилами:

  1. Функция принимает три аргумента: массив, по которому будет происходить итерация, callback фунцию, которая будет выполнена для каждого элемента массива, и значение, которое будет использовано callback функцией в качестве this .
  2. callback функция, в свою очередь, также работает с тремя аргументами: текущий элемент массива, индекс элемента, ссылка на сам массив.
  3. Функция ничего не возвращает.

Самое очевидное решение: используя цикл for перебрать каждый элемент массива и выполнить передаваемую callback функцию с каждым. Сделать это очень просто:

Функция each будет отлично работать, за исключением того, что не соблюдается часть первого правила — для callback функции нельзя задать контекст, то есть нельзя присвоить значение this :

Чтобы можно было осуществлять передачу ключевого слова this в функцию each достаточно воспользоваться методом функций call , который позволяет вызвать функцию и явно указать, на что будет указывать ключевое слово this .

Теперь функция each работает с массивами в точности так, как и метод forEach . Чтобы её протестировать, можно запустить её вместе с кодом из предыдущего примера.

Несмотря на кажущуюся одинаковость метода forEach и написанной нами выше функции each , между ними есть одно очень важное различие: метод forEach работает только с массивами, в то время как функция each может также успешно работать с любыми коллекциями.

Скорее всего, вы уже видели ранее подобную конструкцию, которую используют многие JavaScript разработчики для итерации по всем элементам из DOM коллекции:

Подобная конструкция может ввести в замешательство практически любого программиста, который раньше не использовал её сам. Подробное объяснение, как именно она работает можно найти в этом вопросе на Stackoverflow. Нужна она лишь для того, чтобы провести итерацию по всем элементам выбранной DOM коллекции и выполнить callback функцию для каждого из них. При использовании написанной нами функции each надобности в такой хитрой конструкции нет, так как она поддерживает работу не только с массивами, но и с любыми коллекциями, в том числе и коллекциями DOM элементов:

Когда появится широкая поддежка ES6 метод forEach можно будет использовать с DOM коллекциями более простым способом с помощью оператора Spread:

Метод массивов map похож по своей функциональности на forEach , но результат выполнения callback функции добавляется в новый массив, который возвращается после последней итерации. Другими словами, результатом метода map всегда является новый массив с результатами выполнения функции callback на исходном массиве.

Также, как и в случае с forEach map помимо callback функции принимает второй параметр, который позволяет задать контекст и явно указать this :

Как вы могли заметить, при использовании map и forEach всегда использовалась анонимная функция в качестве callback . Это совсем не обязательно. Вы можете объвить функцию, которую хотите использовать в качестве callback заранее, а затем просто передать её в качестве параметра.

В некоторых ситуациях можно вообще не создавать отдельную функцию и пользоваться нативными методами. Пример выше, в котором каждое число в массиве возводилось в степень, соответствующую своему индексу, можно значительно сократить:

Передвая callback функцию подобным образом вы не теряете возможность явно указать this . Таким образом, метод grabPhrases из функции конструктора Person также можно немного сократить:

В отличие от forEach при использовании map вам становится доступен chaining. Это значит, что вы можете последовательно применить метод на возвращенный после map массив.

map своими руками

Как и при создании аналога forEach напишем небольшие правила, которыми будем руководствоваться при создании функции map :

  1. Функция возвращает новый массив, оставляя исходный без изменений.
  2. Всё, что возвращает callback функция добавляется в новый массив.
  3. Функция принимает три аргумента: массив, по которому будет происходить итерация, callback фунцию, которая будет выполнена для каждого элемента массива, и значение, которое будет использовано callback функцией в качестве this .
  4. callback функция, в свою очередь, также работает с тремя аргументами: текущий элемент массива, индекс элемента, ссылка на сам массив.


Правил стало больше, но последними двумя мы уже пользовались при создании функции each , а, значит, вы уже знаете, как с ними справиться.

Менять исходный массив нельзя, поэтому нужно создать новый массив в самом начале выполнения функции map . Назовём его results . В созданный нами массив results при кажом выполнении будем добавлять результат выполнения функции callback с помощью метода push . После завершения последней итерации всё, что остаётся сделать — вернуть массив results .

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

Небольшой совет: не используйте метод map для манипуляций с коллекциями DOM элементов, например, чтобы добавить класс всем элементам коллекции.

Данный код сработает и всем ссылкам будет добавлен класс link , но пимимо этого будет создан дополнительный пустой массив, что скажется на производительности при большом объеме итерируемой коллекции. В подобных случаях следует использовать исключительно forEach .

Тем не менее, map отлично подходит для получения данных из DOM коллекций. Например, получение всех href атрибутов будет выглядеть следующим образом:

filter

Метод filter , как и следует из названия, служит для фильтрации массива по правилам, заданным в callback функции. Так же, как в случае с map создаётся новый массив, куда добавляются все элементы прошедшие провеку колбэком.

При использовании метода filter результатом выполнения может быть любое значение, но данные из исходного массива будут добавлены только в том случае, если значение является правдивым. Напомню, что правдивыми значениями являются все, кроме:

  1. пустой строки »
  2. числа ноль 0
  3. false
  4. undefined
  5. null

Тем не менее, пользоваться подобным способом фильтрации массива не стоит в силу его неочевидности. Поэтому рекомендуется создавать callback функцию таким образом, чтобы она всегда возвращала либо true , либо false .

Немного более сложный пример использования метода filter . Допустим, что мы получаем JSON файл с сервера с подобным содержимым:

После получения данных (с помощью AJAX или JSONP, например) мы хотим их отфильтровать, узнав из свойства isCustomer , является ли данных человек нашим клиентом или нет. Вот здесь и пригодится метод filter :

Или же можно руководствоваться немного другим, более сложным принципом при выборе цели. Например, мы решили, что если email клиента не начинается с буквы j , то он определённо нам не подходит.

filter своими руками

Как и для прошлых функций map и forEach напишем небольшой свод правил:

  1. Функция возвращает новый массив, оставляя исходный без изменений.
  2. Данные исходного массива передаются в callback функцию. Результат выполнения callback функции решает будет ли добавлен данный элемент в новый массив.
  3. Функция принимает три аргумента: массив, по которому будет происходить итерация, callback фунцию, которая будет выполнена для каждого элемента массива, и значение, которое будет использовано callback функцией в качестве this .
  4. callback функция, в свою очередь, также работает с тремя аргументами: текущий элемент массива, индекс элемента, ссылка на сам массив.

С помощью всё того же метода функций call мы вызываем callback функцию, но на этот раз всё, что нас будет интересовать — вернула ли функция правдивое значение. Если результат содержит правдивое значение, то данные будут добавлены в массив, если же нет, то просто проигнорированы.

Удостоверимся, что функция filter работает, как мы её и задумывали:

Небольшой хак: чтобы отфильтровать все ложные значения из массива можно воспользоваться конструктором Boolean :

some и every

Методы some и every во многом похожи друг на друга. Оба метода возвращают true или false . some возвращает true тогда, когда хотя бы один элемент массива отвечает переданным в callback функцию условиям. every вернёт true , когда все элементы массива отвечают данным условиям. Звучит грозно, но, на самом деле всё очень просто.

Методы some и every очень удобно использовать вместе с методом filter для вложенных массивов. Чтобы понять, как это работает, опять представим, что мы получили данные с сервера в виде JSON файла, который содержит массив объектов (наших покупателей). У каждого покупателя есть свойство purchases , которое представляет собой список приобретённых покупателем товаров в нашем магазине ранее.

Чтобы понять, какие клиенты нам принесли больше всего прибыли, мы хотим их отфильтровать и посмотреть, кто из них покупал у нас технику от Apple (iPhone, Macbook или iMac). Метод some поможет сделать это с помощью всего нескольких строк кода.

some и every своими руками
  1. Функция возвращает только true или false
  2. Каждое значение передаётся в callback функцию и на результате её выполнения для всех элементов массива решается, какой будет результат.
  3. Функция принимает три аргумента: массив, по которому будет происходить итерация, callback фунцию, которая будет выполнена для каждого элемента массива, и значение, которое будет использовано callback функцией в качестве this .
  4. callback функция, в свою очередь, также работает с тремя аргументами: текущий элемент массива, индекс элемента, ссылка на сам массив.

Функция some при каждой итерации проверяет, является ли результат выполнения callback функции правдивым. Если она находит хотя бы один правдивый результат, то прерывает своё выполнение и сразу возвращает true .

Функция every построена по противоположному принципу. Если хотя бы одно значение не является верным, то сразу же возвращается false без дальнейшего перебирания массива.

Функции every и some работают идентично соответствующим им методам и будут давать одинаковые резутаты. Тем не менее, написанные нами функции работают лучше нативных методов. Почему? Используя методы массивов some и every вы подразумеваете, что callback функция будет выполнена для всех элементов без исключения. Но, может оказаться так, что первый элемент в массиве уже содержит нужные нам данные и итерация по всем остальным будет абсолютно бесполезной. В написанных нами функциях таких итераций не будет — когда будет найдено искомое значение функция сразу же прекратит своё выполнение. Подобный подход может дать достаточно ощутимый прирост производительности при работе с большими объемами данных, например, с JSON файлами содержащими несколько тысяч объектов.

reduce

callback функция всех рассмотренных выше методов массивов работает с одинаковым набором данных: значением, индексом и массивом. Метод reduce не такой, как все. Принцип его работы немного отличается от всех остальных методов. Начнём сразу с примера:

Метод reduce принимает два аргумента callback функцию и начальное значение, которое будет присвоено аргументу result в примере выше при первой итерации. callback функция принимает целых 4 аргумента: промежуточное значение (аргумент result в примере выше), элемент массива, индекс элемента и сам массив. После каждой итерации в промежуточное значение записываются новые данные, которые берутся из результата выполнения функции callback при прошлой итерации:

Разумеется, reduce может работать с любыми типами данных, не только с числами. Пример со строками (в данном случае в качестве начального значения стоит передавать пустую строку):

Пример с многомерным массивом (начинаем с пустого массива):

reduce своими руками

Вы уже знаете — у нас есть правила:

  1. Функция принимает три аргумента: массив, callback функцию, начальное значение.
  2. После каждой итерации в промежуточное значение перезаписывается значением, полученным в результате выполнения callback функции. 3. callback функция принимает четыре аргумента: промежуточное значение, текущий элемент массива, индекс элемента, ссылка на сам массив.
  3. Явно указать значение this нельзя.

В нативном методе указывать значение this нельзя, поэтому вместо введения в функцию ещё одного аргумента thisArg мы просто передаём null в вызов функции.

Протестируем написанную нами функцию reduce на предыдущих примерах, чтобы убедиться, что всё работает, как мы и ожидаем.


Выйти из Collection.forEach() по условию?

Я понимаю, что после появления Java 8 прошло уже много времени, но тем не менее сценарий использования Collection.forEach() везде предлагается один и тот же — мы проходим по всем элементам коллекции и с каждым элементов что-то делаем.

А если мне нужно реализовать другой достаточно типичный сценарий. Цель — идти ровно до тех пор, пока я чего-то не обнаружу в коллекции:

Есть ли возможность это сделать в стиле Java 8?

Ещё часто в таких циклах мне хотелось бы сделать выход из метода:

Array.prototype.forEach()

На этой странице

Метод forEach() выполняет указанную функцию один раз для каждого элемента в массиве.

Источник этого интерактивного примера хранится в GitHub. Если вы хотите внести вклад в проект интерактивных примеров, пожалуйста, клонируйте https://github.com/mdn/interactive-examples и отправьте нам pull request.

Синтаксис

Параметры

Возвращаемое значение

Описание

Метод forEach() выполняет функцию callback один раз для каждого элемента, находящегося в массиве в порядке возрастания. Она не будет вызвана для удалённых или пропущенных элементов массива. Однако, она будет вызвана для элементов, которые присутствуют в массиве и имеют значение undefined .

Функция callback будет вызвана с тремя аргументами:

  • значение элемента (value)
  • индекс элемента (index)
  • массив, по которому осуществляется проход (array)

Если в метод forEach() был передан параметр thisArg , при вызове callback он будет использоваться в качестве значения this . В противном случае, в качестве значения this будет использоваться значение undefined . В конечном итоге, значение this , наблюдаемое из функции callback , определяется согласно обычным правилам определения this , видимого из функции.

Диапазон элементов, обрабатываемых методом forEach() , устанавливается до первого вызова функции callback . Элементы, добавленные в массив после начала выполнения метода forEach() , не будут посещены функцией callback . Если существующие элементы массива изменятся, значения, переданные в функцию callback , будут значениями на тот момент времени, когда метод forEach() посетит их; удалённые элементы посещены не будут.

Примечание: Не существует способа остановить или прервать цикл forEach() кроме как выбрасыванием исключения. Если это требуется, метод forEach() неправильный выбор. Используйте обычные циклы. Если нужно протестировать элементы массива на условие и нужно вернуть булевое значение, вы должны воспользоваться методами Array.prototype.every() или Array.prototype.some() .

Метод forEach() выполняет функцию callback один раз для каждого элемента массива; в отличие от методов every() и some() , он всегда возвращает значение undefined .

Примеры

Конвертируем for в forEach

Пример: печать содержимого массива

Следующий код выводит каждый элемент массива на новой строке журнала:

BestProg

Расширенный цикл for в стиле for each . Общая форма. Примеры. Обработка массивов, коллекций объектов ArrayList , LinkedList

Содержание

1. Назначение, общая форма и принцип работы расширенного цикла for

В программах на Java очень часто нужно реализовывать обработку массивов, коллекций, наборов данных. Наиболее распространенной операцией при обработке массивов данных есть перебор каждого элемента массива. Для обеспечения перебора используется один из известных операторов цикла ( for , while , do…while ). Наиболее часто для перебора элементов массива используется цикл for .

Поскольку, перебор элементов массива есть стандартной операцией, то разработчики Java ввели дополнительный формат цикла for , который обеспечивает более упрощенную, рациональную его реализацию. В этой реализации общая форма цикла for подобна к форме цикла foreach в других языках программирования.

Общая форма расширенного цикла for следующая:

  • type – тип внутренней переменной с именем variable ;
  • variable – внутренняя переменная типа type, которая имеет видимость в границах блока фигурных скобок < >. Эта переменная есть итерационной, она сохраняет элементы набора данных collection ;
  • collection – набор данных, которые по-очереди перебираются в цикле. Набором данных может служить коллекция или массив.

Расширенный цикл for работает следующим образом. В расширенном цикле for происходит последовательный перебор всех элементов коллекции или массива. Количество итераций цикла равна количеству элементов в коллекции или массиве. На каждой итерации значение элемента массива помещается в итерационную переменную variable , которую можно обрабатывать или использовать. Цикл гарантирует прохождение всей коллекции (массива) от первого элемента до последнего.

2. Примеры обработки массивов расширенным циклом for
2.1. Пример. Обработка массива чисел типа int (примитивный тип данных).

В примере демонстрируется использование расширенного цикла for для обработки одномерного целочисленного массива A . Реализован подсчет числа парных чисел массива.

2.2. Пример. Обработка массива объектов типа Book (книга)

Пусть задан класс Book , реализующий книгу

Использование расширенного цикла for для массива типа Book может быть, например, следующим:

В результате выполнения вышеприведенного кода будут выведены строки

3. Примеры обработки коллекций расширенным циклом for


3.1. Пример. Обработка коллекции типа Integer с помощью динамического массива ArrayList

В примере демонстрируется обработка коллекции целых чисел с помощью классов ArrayList и Integer .

Класс ArrayList реализует динамический массив, который может увеличиваться или уменьшаться по мере необходимости. Чтобы использовать данный класс нужно подключить пакет java.util . Объявление класса ArrayList следующее:

  • E – тип объектов, которые сохраняются в массиве.

Класс Integer есть классом-оберткой над типом int , который представляет целочисленные значения. В данном примере класс ArrayList для обработки объектов типа Integer имеет вид:

Демонстрация использования расширенного цикла for для коллекции целых чисел с помощью класса ArrayList может быть, например, такой

3.2. Обработка коллекции типа Book с помощью связного списка LinkedList

Пусть задан класс (тип) Book , который имеет следующее объявление:

В данном примере для демонстрации расширенного цикла for используется класс LinkedList . Этот класс предназначен для организации данных заданного типа в связный список. Чтобы использовать класс LinkedList в программе нужно подключить пакет java.util . Объявление класса имеет вид:

  • E – тип объектов, которые сохраняются в списке. В нашем случае этим типом есть класс Book .

Программный код, который формирует список книг типа Book и обрабатывает его в расширенном цикле for (поиск заданной книги) имеет вид:

В вышеприведенном коде в строках

номер позиции в связном списке Book вычисляется путем введения дополнительной переменной i . Бывают случаи, когда нужно обрабатывать не все элементы коллекции. По значению итератора i можно определять номер позиции объекта коллекции, который может быть обработан.

После выполнения вышеприведенного кода на экран будет выведено:

4. Какие преимущества и недостатки применения расширенного цикла for для массивов

Расширенный цикл for есть удобным для обработки коллекций. Использование расширенного цикла for дает следующие преимущества:

  • упрощенность и рациональность представления в сравнении с циклом for ;
  • не нужно использовать дополнительную переменную цикла, задавать ее начальное значение и условие завершения цикла;
  • не нужно индексировать массив.

Основной недостаток расширенного цикла for :

  • отсутствие «гибкости» в оперировании итерационной переменной в случаях, когда нужно осуществлять перебор не всех элементов коллекции. Например, если нужно перебирать только первые n элементов всей коллекции или элементы, которые лежат на определенных позициях (четных позициях, нечетных позициях) в коллекции. Однако, этот недостаток можно обойти введением дополнительных переменных-итераторов и проверки соответствующих условий.

Собеседование по Java — коллекции (Collections) (вопросы и ответы)

Список вопросов и ответов по теме “Коллекции в Java”.

К списку вопросов по всем темам

Вопросы

1. Дайте определение понятию “коллекция”.
2. Назовите преимущества использования коллекций.
3. Какие данные могут хранить коллекции?
4. Какова иерархия коллекций?
5. Что вы знаете о коллекциях типа List?
6. Что вы знаете о коллекциях типа Set?
7. Что вы знаете о коллекциях типа Queue?
8. Что вы знаете о коллекциях типа Map, в чем их принципиальное отличие?
9. Назовите основные реализации List, Set, Map.
10. Какие реализации SortedSet вы знаете и в чем их особенность?
11. В чем отличия/сходства List и Set?
12. Что разного/общего у классов ArrayList и LinkedList, когда лучше использовать ArrayList, а когда LinkedList?
13. В каких случаях разумно использовать массив, а не ArrayList?
14. Чем отличается ArrayList от Vector?
15. Что вы знаете о реализации классов HashSet и TreeSet?
16. Чем отличаются HashMap и TreeMap? Как они устроены и работают? Что со временем доступа к объектам, какие зависимости?
17. Что такое Hashtable, чем она отличается от HashMap? На сегодняшний день она deprecated, как все-таки использовать нужную функциональность?
18. Что будет, если в Map положить два значения с одинаковым ключом?
19. Как задается порядок следования объектов в коллекции, как отсортировать коллекцию?
20. Дайте определение понятию “итератор”.
21. Какую функциональность представляет класс Collections?
22. Как получить не модифицируемую коллекцию?
23. Какие коллекции синхронизированы?
24. Как получить синхронизированную коллекцию из не синхронизированной?
25. Как получить коллекцию только для чтения?
26. Почему Map не наследуется от Collection?
27. В чем разница между Iterator и Enumeration?
28. Как реализован цикл foreach?
29. Почему нет метода iterator.add() чтобы добавить элементы в коллекцию?
30. Почему в классе iterator нет метода для получения следующего элемента без передвижения курсора?
31. В чем разница между Iterator и ListIterator?
32. Какие есть способы перебора всех элементов List?
33. В чем разница между fail-safe и fail-fast свойствами?
34. Что делать, чтобы не возникло исключение ConcurrentModificationException?
35. Что такое стек и очередь, расскажите в чем их отличия?
36. В чем разница между интерфейсами Comparable и Comparator?
37. Почему коллекции не наследуют интерфейсы Cloneable и Serializable?

Ответы

Тема коллекций невероятно обширная и для того, чтобы ответить на каждый вопрос глубоко нужна отдельная статья почти под каждый вопрос. При проработке этого раздела рекомендую прочитать дополнительный материал, указанный в ответах.

1. Дайте определение понятию “коллекция”.

Коллекциями/контейнерами в Java принято называть классы, основная цель которых – хранить набор других элементов.

2. Назовите преимущества использования коллекций.

Массивы обладают значительными недостатками. Одним из них является конечный размер массива, как следствие, необходимость следить за размером массива. Другим — индексная адресация, что не всегда удобно, т.к. ограничивает возможности добавления и удаления объектов. Чтобы избавиться от этих недостатков уже несколько десятилетий программисты используют рекурсивные типы данных, такие как списки и деревья. Стандартный набор коллекций Java служит для избавления программиста от необходимости самостоятельно реализовывать эти типы данных и снабжает его дополнительными возможностями.

3. Какие данные могут хранить коллекции?

Коллекции могут хранить любые ссылочные типы данных.

4. Какова иерархия коллекций?

Здесь следует обратить внимание, что interface Map не входит в иерархию interface Collection.

С Java 1.6 классы TreeSet и TreeMap имплементируют интерфейсы NavigableSet и NavigableMap, которые расширяют интерфейсы SortedSet и SortedMap соответственно (SortedSet и SortedMap расширяют Set и Map).

Подробная статья про коллекции с описанием основных методов: http://www.quizful.net/post/Java-Collections

5. Что вы знаете о коллекциях типа List?

List — это упорядоченный список. Объекты хранятся в порядке их добавления в список. Доступ к элементам списка осуществляется по индексу.

6. Что вы знаете о коллекциях типа Set?


Set — множество неповторяющихся объектов. В коллекции этого типа разрешено наличие только одной ссылки типа null .

7. Что вы знаете о коллекциях типа Queue?

Queue — коллекция, предназначенная для хранения элементов в порядке, нужном для их обработки. В дополнение к базовым операциям интерфейса Collection, очередь предоставляет дополнительные операции вставки, получения и контроля.

Очереди обычно, но не обязательно, упорядочивают элементы в FIFO (first-in-first-out, «первым вошел — первым вышел») порядке.

Метод offer() вставляет элемент в очередь, если это не удалось — возвращает false . Этот метод отличается от метода add() интерфейса Collection тем, что метод add() может неудачно добавить элемент только с использованием unchecked исключения.

Методы remove() и poll() удаляют верхушку очереди и возвращают ее. Какой элемент будет удален (первый или последний) зависит от реализации очереди. Методы remove() и poll() отличаются лишь поведением, когда очередь пустая: метод remove() генерирует исключение, а метод poll() возвращает null .

Методы element() и peek() возвращают (но не удаляют) верхушку очереди.

java.util.Queue реализует FIFO–буфер. Позволяет добавлять и получать объекты. При этом объекты могут быть получены в том порядке, в котором они были добавлены.

Реализации: java.util.ArrayDeque , java.util.LinkedList .

java.util.Deque наследует java.util.Queue . Двунаправленная очередь. Позволяет добавлять и удалять объекты с двух концов. Так же может быть использован в качестве стека.

Реализации: java.util.ArrayDeque , java.util.LinkedList .

Подробнее http://www.seostella.com/ru/article/2012/08/09/kollekcii-collections-v-java-queue.html

8. Что вы знаете о коллекциях типа Map, в чем их принципиальное отличие?

Интерфейс java.util.Map используется для отображения каждого элемента из одного множества объектов (ключей) на другое (значений). При этом, каждому элементу из множества ключей ставится в соответствие множество значений. В то же время одному элементу из множества значений может соответствовать 1, 2 и более элементов из множества ключей. Интерфейс java.util.Map описывает функциональность ассоциативных массивов.

Реализации: java.util.HashMap , java.util.LinkedHashMap , java.util.TreeMap , java.util.WeakHashMap .

java.util.SortedMap наследует java.util.Map . Реализации этого интерфейса обеспечивают хранение элементов множества ключей в порядке возрастания (см. java.util.SortedSet). Реализации: java.util.TreeMap .

http://developer.alexanderklimov.ru/andro > Интерфейс Класс/Реализация Описание List ArrayList Список LinkedList Список Vector Вектор Stack Стек Set HashSet Множество TreeSet Множество SortedSet Отсортированное множество Map HashMap Карта/Словарь TreeMap Карта/Словарь SortedMap Отсортированный словарь Hashtable Хеш-таблица

10. Какие реализации SortedSet вы знаете и в чем их особенность?

java.util.SortedSet наследует java.util.Set . Реализации этого интерфейса, помимо того что следят за уникальностью хранимых объектов, поддерживают их в порядке возрастания. Отношение порядка между объектами может быть определено, как с помощью метода compareTo интерфейса java.lang.Comparable , так и при помощи специального класса-компаратора, наследующего интерфейс java.util.Comparator .

Реализации: java.util.TreeSet — коллекция, которая хранит свои элементы в виде упорядоченного по значениям дерева. TreeSet инкапсулирует в себе TreeMap, который в свою очередь использует сбалансированное бинарное красно-черное дерево для хранения элементов. TreeSet хорош тем, что для операций add, remove и contains потребуется гарантированное время log(n).

11. В чем отличия/сходства List и Set?

Оба унаследованы от Collection , а значит имеют одинаковый набор и сигнатуры методов. List хранит объекты в порядке вставки, элемент можно получить по индексу. Set не может хранить одинаковых элементов.

12. Что разного/общего у классов ArrayList и LinkedList, когда лучше использовать ArrayList, а когда LinkedList?

ArrayList реализован внутри в виде обычного массива . Поэтому при вставке элемента в середину, приходится сначала сдвигать на один все элементы после него, а уже затем в освободившееся место вставлять новый элемент. Зато в нем быстро реализованы взятие и изменение элемента – операции get, set , так как в них мы просто обращаемся к соответствующему элементу массива.

LinkedList реализован внутри по-другому. Он реализован в виде связного списка : набора отдельных элементов, каждый из которых хранит ссылку на следующий и предыдущий элементы. Чтобы вставить элемент в середину такого списка, достаточно поменять ссылки его будущих соседей. А вот чтобы получить элемент с номером 130, нужно пройтись последовательно по всем объектам от 0 до 130. Другими словами операции set и get тут реализованы очень медленно . Посмотри на таблицу:

Описание Операция ArrayList LinkedList
Взятие элемента get Быстро Медленно
Присваивание элемента set Быстро Медленно
Добавление элемента add Быстро Быстро
Вставка элемента add(i, value) Медленно Быстро
Удаление элемента remove Медленно Быстро

Если необходимо вставлять (или удалять) в середину коллекции много элементов, то лучше использовать LinkedList. Во всех остальных случаях – ArrayList.

LinkedList требует больше памяти для хранения такого же количества элементов, потому что кроме самого элемента хранятся еще указатели на следующий и предыдущий элементы списка, тогда как в ArrayList элементы просто идут по порядку

Из лекции javarush.ru
Структуры данных в картинках. LinkedList: http://habrahabr.ru/post/127864/

13. В каких случаях разумно использовать массив, а не ArrayList?

Если коротко, то Oracle пишет — используйте ArrayList вместо массивов. Если ответить на этот вопрос нужно по-другому, то можно сказать следующее: массивы могут быть быстрее и кушать меньше памяти. Списки теряют в производительности из-за возможности автоматического увеличения размера и сопутствующих проверок. Плюс к этому, что размер списка увеличивается не на 1, а на большее кол-во элементов (+15)*. Так же доступ к [10] в массиве может быть быстрее чем вызов get(10) у списка.

*Читатель прислал комментарий «У ArrayList увеличение происходит в 1.5 раза. int newCapacity = oldCapacity + (oldCapacity >> 1);».

Структуры данных в картинках. ArrayList: http://habrahabr.ru/post/128269/
Еще о ArrayList на сайте http://developer.alexanderklimov.ru/android/java/arraylist.php

14. Чем отличается ArrayList от Vector?

Vector deprecated. У Vector некоторые методы синхронизированы и поэтому они медленные. В любом случае Vector не рекомендуется использовать вообще.

15. Что вы знаете о реализации классов HashSet и TreeSet?

Название Hash… происходит от понятия хэш-функция. Хэш-функция — это функция, сужающая множество значений объекта до некоторого подмножества целых чисел. Класс Object имеет метод hashCode() , который используется классом HashSet для эффективного размещения объектов, заносимых в коллекцию. В классах объектов, заносимых в HashSet , этот метод должен быть переопределен (override).

HashSet реализован на основе хеш-таблицы, а TreeSet — на основе бинарного дерева.

Подробнее о Set, HashSet, LinkedHashSet, TreeSet: http://developer.alexanderklimov.ru/android/java/set.php

HashSet гораздо быстрее чем TreeSet (константное время против логарифмического для большинства операций, таких как add , remove , contains ), но TreeSet гарантирует упорядоченность объектов. Оба не синхронизированы.

  • предоставляет константное время для add() , remove() , contains() и size()
  • порядок элементов в контейнере может меняться
  • производительность итерации по контейнеру зависит от емкости и «коэффициента загрузки» (рекомендуется оставлять load factor значением по умолчанию равным 0.75, что является хорошим компромиссом между временем доступа и объемом хранимых данных)


  • время для базовых операций add() , remove() , contains() — log(n)
  • гарантирует порядок элементов
  • не предоставляет каких-либо параметров для настройки производительности
  • предоставляет дополнительные методы для упорядоченного списка: first() , last() , headSet() , tailSet() и т.д.
Годный ответ на StackOverflow http://stackoverflow.com/questions/1463284/hashset-vs-treeset

16. Чем отличаются HashMap и TreeMap? Как они устроены и работают? Что со временем доступа к объектам, какие зависимости?

В целом ответ про HashSet и TreeSet подходит и к этому вопросу.

HashMap работает строго быстрее TreeMap .

TreeMap реализован на красно-черном дереве, время добавления/поиска/удаления элемента — O(log N), где N — число элементов в TreeMap на данный момент.

У HashMap время доступа к отдельному элементу — O(1) при условии, что хэш-функция ( Object.hashCode() ) определена нормально (что является правдой в случае Integer ).

Общая рекомендация — если не нужна упорядоченность, использовать HashMap . Исключение — ситуация с вещественными числами, которые в качестве ключей почти всегда очень плохи. Для них нужно использовать TreeMap , предварительно поставив ему компаратор, который сравнивает вещественные числа так, как это нужно в данной задаче. Например, для обычных геометрических задач два вещественных числа могут считаться равными, если отличаются не более, чем на 1e-9.

Структуры данных в картинках. HashMap: http://habrahabr.ru/post/128017/

17. Что такое Hashtable, чем она отличается от HashMap? На сегодняшний день она deprecated, как все-таки использовать нужную функциональность?

Некоторые методы HashTable синхронизированы, поэтому она медленнее HashMap .

  • HashTable синхронизирована, а HashMap нет.
  • HashTable не позволяет иметь null ключи или значения. HashMap позволяет иметь один null ключ и сколько угодно null значений.
  • У HashMap есть подкласс LinkedHashMap , который добавляет возможности по итерации. Если вам нужна эта функциональность, то можно легко переключаться между классами.

Общее замечание — не рекомендуется использовать HashTable даже в многопоточных приложениях. Для этого есть ConcurrentHashMap .

http://stackoverflow.com/questions/40471/differences-between-hashmap-and-hashtable

18. Что будет, если в Map положить два значения с одинаковым ключом?

Последнее значение перезапишет предыдущее.

19. Как задается порядок следования объектов в коллекции, как отсортировать коллекцию?

Класс ТгееМар полностью реализует интерфейс SortedMap . Он реализован как бинарное дерево поиска, значит его элементы хранятся в упорядоченном виде. Это значительно ускоряет поиск нужного элемента. Порядок задается либо естественным следованием элементов, либо объектом, реализующим интерфейс сравнения Comparator .

В этом классе четыре конструктора:

ТгееМар() — создает пустой объект с естественным порядком элементов;
TreeМар(Comparator с) — создает пустой объект, в котором порядок задается объектом сравнения с;
ТгееМар(Map f) — создает объект, содержащий все элементы отображения f, с естественным порядком его элементов;
ТгееМар(SortedMap sf) — создает объект, содержащий все элементы отображения sf, в том же порядке.

Интерфейс Comparator описывает два метода сравнения:

int compare(Object obj1, object obj2) — возвращает отрицательное число, если obj1 в каком-то смысле меньше obj2 ; нуль, если они считаются равными; положительное число, если obj1 больше obj2 . Для читателей, знакомых с теорией множеств, скажем, что этот метод сравнения обладает свойствами тождества, антисимметричности и транзитивности;

boolean equals(Object obj) — сравнивает данный объект с объектом obj , возвращая true , если объекты совпадают в каком-либо смысле, заданном этим методом.

Для каждой коллекции можно реализовать эти два метода, задав конкретный способ сравнения элементов, и определить объект класса SortedMap вторым конструктором. Элементы коллекции будут автоматически отсортированы в заданном порядке.

В Java 8, Почему массивам не был дан метод forEach Iterable?

должно быть, я что-то упускаю.

в Java 5,оператор»for-each loop» (также называемый расширенным циклом for). Похоже, что он был введен в основном для перебора коллекции. Любой класс коллекции (или контейнера), реализующий Iterable интерфейс имеет право на итерации с помощью «для каждого цикла». Возможно, по историческим причинам массивы Java не реализовали Iterable взаимодействие. Но поскольку массивы были / вездесущи, javac примет использование цикла for-each на массивах (генерируя байт-код, эквивалентный традиционному циклу for).

в Java 8, forEach метод добавлен Iterable интерфейс как по умолчанию метод. Это сделало возможным передачу лямбда-выражений в коллекции (при итерации) (например, list.forEach(System.out::println) ). Но опять же, массивы не пользуются этим лечением. (Я понимаю, что есть обходные приемы.)

существуют ли технические причины, по которым javac не может быть улучшен для приема массивов в forEach, так же, как он принимает их в расширенном цикле for? Похоже, что генерация кода будет возможна без необходимости реализации массивов Iterable . Я наивен?

это особенно важно для новичка в языке, который довольно естественно использует массивы из-за их синтаксической простоты. Едва ли естественно переключаться на списки и использовать Arrays.asList(1, 2, 3) .

2 ответов

есть куча особых случаев на языке Java и в JVM для массивов. Массивы имеют API, но он едва виден. Это похоже на то, что массивы объявляются имеющими:

  • implements Cloneable, Serializable
  • public final int length
  • public T[] clone() здесь T — тип компонента массива

однако эти объявления не отображаются ни в одном исходном коде. См.JLS 4.10.3 и JLS 10.7 для объяснения. Cloneable и Serializable видны через отражение и возвращаются вызовом

как ни странно, в и clone() метод не виден рефлективно. The length поле не является полем вообще; использование его превращается в специальный arraylength байткод. Вызов clone() приводит к фактическому вызову виртуального метода, но если приемник является типом массива, это обрабатывается специально JVM.

примечательно, однако, array классы не реализуют Iterable интерфейс.

когда цикл enhanced-for («for-each») был добавлен в Java SE 5, он поддерживал два разных случая для выражения правой стороны: Iterable или тип массива (JLS 14.14.2). Причина в том, что Iterable экземпляры и массивы обрабатываются совершенно по-разному оператором enhanced-for. Этот раздел JLS дает полное лечение, но проще говоря, ситуация такова, как следует.

на Iterable iterable код

синтаксический сахар для


синтаксический сахар для

теперь, почему это было сделано таким образом? Конечно, массивы могли бы реализовать Iterable , так как они уже реализуют другие интерфейсы. Компилятор также может синтезировать Iterator реализация, которая поддерживается матрица. (Для этого есть прецедент. Компилятор уже синтезирует static values() и valueOf() методы, которые автоматически добавляется к каждому enum класс, как описано в JLS 8.9.3.)

но массивы являются очень низкоуровневой конструкцией и обращаются к массиву с помощью int ожидается, что значение будет чрезвычайно недорогой операцией. Это довольно идиоматично для запуска индекса цикла из 0 к длине массива, увеличиваясь на единицу каждый раз. Увеличенный-для loop на массиве делает именно это. Если цикл enhanced-for над массивом был реализован с использованием Iterable протокол, я думаю, большинство людей были бы неприятно удивлены, обнаружив, что цикл над массивом включает в себя первоначальный вызов метода и выделение памяти (создание Iterator ), а затем два вызова метода на итерацию цикла.

поэтому, когда методы по умолчанию были добавлены в Iterable в Java 8 это вообще не повлияло на массивы.

как отмечали другие, если у вас есть массив int , long , double , или ссылочного типа, можно превратить это в поток, используя один из Arrays.stream() звонки. Это обеспечивает доступ к map() , filter() , forEach() , etc.

было бы неплохо, если бы специальные случаи на языке Java и JVM для массивов были заменены на реальные конструкции (наряду с исправлением кучу других связанных с массивом проблем, таких как плохая обработка 2 + размерных массивов, длина 2^31 ограничение и так далее). Это предмет расследования «массивов 2.0», которое возглавляет Джон Роуз. См. выступление Джона на JVMLS 2012 (видео, горки). Идеи, относящиеся к этому обсуждению, включают введение фактического интерфейса для массивов, чтобы позволить библиотекам вставлять доступ к элементам, поддерживать дополнительные операции, такие как нарезка и копирование, и т. д.

обратите внимание, что все это расследование и будущей работы. Нет ничего из этих улучшений массива, которые зафиксированы в дорожной карте Java для любого выпуска, на момент написания этой статьи (2020-02-23).

предположим, что специальный код будет добавлен в компилятор java для обработки forEach . Тогда можно было бы задать много подобных вопросов. Почему мы не можем написать myArray.fill(0) ? Или myArray.copyOfRange(from, to) ? Или myArray.sort() ? myArray.binarySearch() ? myArray.stream() ? Практически каждый статический метод в Arrays интерфейс может быть преобразован в соответствующий метод класса «массив». Почему разработчики JDK должны остановиться на myArray.forEach() ? Однако, обратите внимание, что каждый такой метод должен быть добавлен не только в спецификацию classlib , но на язык Java Спецификация, которая гораздо более стабильна и консервативна. Также это означало бы, что не только реализация таких методов станет частью спецификации, но и такие классы, как java.util.function.Consumer следует явно упомянуть в JLS (который является аргументом proposed forEach метод). Также заметьте что новые едоки были бы необходимы для того чтобы добавить к стандартная библиотека, как FloatConsumer , ByteConsumer , etc. для соответствующих типов массивов. В настоящее время JLS редко ссылается на типы за пределами java.lang пакет (с некоторыми заметными исключениями как java.util.Iterator ). Это подразумевает некоторый уровень стабильности. Предлагаемое изменение слишком радикально для языка Java.

также обратите внимание, что в настоящее время у нас есть один метод, который может быть вызван для массивов напрямую (и реализация которого отличается от java.lang.Object ): это clone() метод. Он фактически добавляет некоторые грязные части в javac и даже JVM как это должно быть сделано специально везде. Это вызывает ошибки (например, ссылки на метод были неправильно обрабатывается в Java 8 JDK-8056051). Добавление более подобной сложности в javac может ввести еще больше похожих ошибок.

такая функция, вероятно, будет реализован в ближайшем будущем в рамках Arrays 2.0 инициатива. Идея состоит в том, чтобы ввести некоторый суперкласс для массивов, которые будут расположены в библиотеке классов, поэтому новые методы могут быть добавлены только путем написания обычного java-кода без настройки javac / JVM. Однако, это также очень сложная функция, поскольку массивы всегда обрабатывается специально на Java, и, насколько я знаю, пока неизвестно, будет ли он реализован и когда.

JavaScript метод forEach()

Определение и применение

JavaScript метод forEach() позволяет выполнить переданную функцию один раз для каждого элемента в массиве в порядке возрастания индекса.

Обращаю Ваше внимание, что функция обратного вызова, переданная в качестве параметра метода forEach() не будет вызвана для удалённых, или пропущенных элементов массива.

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

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

Поддержка браузерами

Метод Chrome Firefox Opera Safari IExplorer Edge
forEach() Да Да Да Да 9.0 Да

JavaScript синтаксис:

Версия JavaScript

Значения параметров

Параметр Описание
function Функция обратного вызова, которая будет выполнена один раз для каждого элемента в массиве. Функция принимает следующие параметры:
  • currentValue — значение текущего элемента
  • index — индекс массива текущего элемента.
  • arr — массив, к которому принадлежит текущий элемент (по которому происходит проход).

Если в качестве параметра метода передается что-то, что не является объектом функции, то будет вызвано исключение TypeError. Обязательный параметр.

thisValue Объект, на который может ссылаться ключевое слово this внутри функции обратного вызова. Если параметр thisValue не используется, то в качестве значения this будет использоваться undefined (в конечном счете this будет зависеть от обычных правил контекста выполнения функции). Необязательный параметр.

Пример использования

В следующем примере мы рассмотрим как получить сумму всех элементов массива с использованием JavaScript метода forEach():

В следующем примере мы рассмотрим использование второго аргумента метода forEach(), который указывает на объект, на который мы можем ссылаться с использованием ключевого слова this внутри функции обратного вызова:

Вызов удаления в цикле foreach в Java

В Java разрешено ли вызывать удаление в коллекции при повторении через коллекцию с использованием цикла foreach? Например:

В качестве дополнения, является ли законным удаление элементов, которые еще не были повторены? Например,

Чтобы безопасно удалить из коллекции, итерации по ней, вы должны использовать Iterator.

Итераторы, возвращаемые этим классом итератором и listIterator методы не работают быстро: если список структурно модифицирован в любом время после создания итератора, любым способом, за исключением итератор самостоятельно удаляет или добавляет методы, итератор будет бросать ConcurrentModificationException. Таким образом, перед лицом параллельных модификации, итератор выходит из строя быстро и чисто, а не рискуя произвольным, недетерминированным поведением в неопределенное время в будущем.

Возможно, для многих новичков непонятно, что итерация по списку с использованием конструкций for/foreach неявно создает итератор, который обязательно недоступен. Эту информацию можно найти здесь

Вы не хотите этого делать. Это может вызвать поведение undefined в зависимости от коллекции. Вы хотите напрямую использовать Iterator. Хотя для каждой конструкции используется синтаксический сахар и на самом деле используется итератор, он скрывает его от вашего кода, поэтому вы не можете получить к нему доступ, чтобы вызвать Iterator.remove .

Поведение итератора неопределенный, если основной коллекция изменяется, итерация продолжается каким-либо образом кроме вызова этого метода.

Вместо этого напишите свой код:

Обратите внимание, что код вызывает Iterator.remove , а не List.remove .

Добавление:

Даже если вы удаляете элемент, который еще не был итерирован, вы все равно не хотите изменять коллекцию, а затем использовать Iterator . Он может модифицировать коллекцию таким образом, что это удивительно, и влияет на будущие операции на Iterator .

Конструкция java «расширенного цикла» заключалась в том, чтобы не подвергать итератору код, но единственным способом безопасного удаления элемента является доступ к итератору. Поэтому в этом случае вам нужно сделать это в старой школе:

Если в реальном коде цикл повышенного цикла действительно стоит того, вы можете добавить элементы во временную коллекцию и вызывать removeAll в списке после цикла.

EDIT (re addendum): Нет, изменение списка каким-либо образом вне метода iterator.remove(), в то время как итерация вызовет проблемы. Единственный способ обойти это — использовать CopyOnWriteArrayList, но это действительно предназначено для проблем concurrency.

Самый дешевый (с точки зрения строк кода) способ удаления дубликатов — это сброс списка в LinkedHashSet (а затем обратно в список, если вам нужно). Это сохраняет порядок вставки при удалении дубликатов.

Цукерберг рекомендует:  Движение по странице без полосы прокрутки
Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих