Автор оригинала: Pankaj Kumar.
Итерация является одним из самых основных требований в любом языке программирования, и, прежде всего, “для” является наиболее широко используемым циклом в java для итерации. Мы увидим эволюцию java для методов итерации циклов.
Java для эволюции циклов
Мы будем работать над примером отображения имен актеров из коллекции . Для простоты давайте возьмем список и настроим его:
Listactors = Arrays.asList("Jack Nicholson", "Marlon Brando", "Robert De Niro", "Al Pacino", "Tom Hanks");
Давайте начнем рассматривать эволюцию цикла for в разных версиях java.
Базовый для цикла
Основными элементами цикла “для” являются инициализация, условие завершения и логика приращения цикла. Давайте посмотрим образец здесь:
for (int i = 0; i < actors.size(); i++) { System.out.println(actors.get(i)); }
Если мы хотим выполнить итерацию по коллекции, которая не относится к типу List, у нас не будет метода get(int index)
, который даст нам индексированное значение из коллекции. Следовательно, в Java 1.2 Были введены итераторы . Давайте посмотрим, как мы решаем ту же проблему с итераторами:
for (Iteratoriterator = actors.iterator(); iterator.hasNext();) { System.out.println(iterator.next()); }
для каждого (Улучшено для цикла)
Этот цикл был введен в Java 5, он устраняет беспорядок, церемонию и возможность ошибки, полностью скрывая итератор или переменную индекса. Давайте посмотрим на это в действии:
for (String actor : actors) { System.out.println(actor); }
Приведенные выше примеры относятся к типам внешних итераторов, здесь логика управления и завершения находится во внешнем итераторе. Это может привести к общей сложности и может привести к ошибкам.
Внутренний цикл forEach
В Java 8 , с внедрением функционального интерфейса и лямбды , архитекторы Java предоставили нам внутренний итератор (цикл forEach), поддерживаемый платформой Collection, и его можно использовать с объектом collections. Этот метод выполняет заданное действие для каждого элемента коллекции. Давайте рассмотрим это более подробно:
Подпись метода : публичная недействительность для каждого(действие потребителя)
Для каждого используется реализация Потребитель
(Функциональный интерфейс), которая имеет прием(T t)
метод для выполнения логики внутри цикла. Давайте посмотрим на реализацию:
actors.forEach(new Consumer() { public void accept(String actor) { System.out.println(actor); } });
Здесь методу forEach присваивается анонимный внутренний класс пользовательского интерфейса . Этот класс имеет метод accept(T t) , который вызывается для всех значений коллекции. Этот код страдает недостатком дизайна, когда каждый раз при использовании forEach создается новый класс в обход анонимного внутреннего класса.
ForLoopEvolution$1.class ForLoopEvolution.class
Это решение выглядит более подробным и сложным, чем предыдущие циклы. Давайте попробуем переработать это упрощенным способом. Вся реализация функционального интерфейса может быть написана в виде лямбда-функции, что более интуитивно понятно. Давайте посмотрим на это в действии:
actors.forEach((e) -> { System.out.println(e); });
Для каждого элемента e актер отображается в консоли. Этот код можно упростить еще больше, удалив ненужные фигурные скобки и скобки.
actors.forEach(e -> System.out.println(e));
Основное преимущество использования лямбда-кода вместо анонимного внутреннего класса заключается в том, что нет загрязнения байт-кода, нет создания класса, вместо этого вызов лямбда-кода откладывается до времени выполнения. Давайте посмотрим байт-код для получения более подробной информации:
invokedynamic помогает JVM создать байт-код из лямбда-выражения и отложить выполнение до времени выполнения.
Вывод метода
Вывод метода дополнительно устраняет некоторые дополнительные церемонии и многословие. Давайте проведем рефакторинг, чтобы передать вывод метода вместо лямбда:
actors.forEach(System.out::println);
Одним из дополнительных преимуществ использования внутреннего итератора является то, что код почти готов к параллелизму. В Java 8 с внедрением потокового api мы можем запускать код параллельно без необходимости синхронизации. Из коллекции можно создать параллельный поток для обработки нашей функциональности.
actors.parallelStream().forEach(System.out::println);
Резюме
Внутренние итераторы менее сложны и менее подвержены ошибкам, их лучше обслуживать. Код с использованием внутреннего итератора можно легко сделать параллельным.
Ссылка на код : URL-адрес GitHub для Java-кода