Java. Расширенный цикл for в стиле for each. Общая форма. Примеры. Обработка массивов, коллекций объектов ArrayList, LinkedList
Расширенный цикл for в стиле for each . Общая форма. Примеры. Обработка массивов, коллекций объектов ArrayList , LinkedList
Поиск на других ресурсах:
1. Назначение, общая форма и принцип работы расширенного цикла for
В программах на Java очень часто нужно реализовывать обработку массивов, коллекций, наборов данных. Наиболее распространенной операцией при обработке массивов данных есть перебор каждого элемента массива. Для обеспечения перебора используется один из известных операторов цикла ( for , while , do…while ). Наиболее часто для перебора элементов массива используется цикл for .
Поскольку, перебор элементов массива есть стандартной операцией, то разработчики Java ввели дополнительный формат цикла for , который обеспечивает более упрощенную, рациональную его реализацию. В этой реализации общая форма цикла for подобна к форме цикла foreach в других языках программирования.
Общая форма расширенного цикла for следующая:
for (type variable : collection) < // некоторые операторы // . >
- type – тип внутренней переменной с именем variable ;
- variable – внутренняя переменная типа type, которая имеет видимость в границах блока фигурных скобок < >. Эта переменная есть итерационной, она сохраняет элементы набора данных collection ;
- collection – набор данных, которые по-очереди перебираются в цикле. Набором данных может служить коллекция или массив.
Расширенный цикл for работает следующим образом. В расширенном цикле for происходит последовательный перебор всех элементов коллекции или массива. Количество итераций цикла равна количеству элементов в коллекции или массиве. На каждой итерации значение элемента массива помещается в итерационную переменную variable , которую можно обрабатывать или использовать. Цикл гарантирует прохождение всей коллекции (массива) от первого элемента до последнего.
2. Примеры обработки массивов расширенным циклом for
2.1. Пример. Обработка массива чисел типа int (примитивный тип данных).
В примере демонстрируется использование расширенного цикла for для обработки одномерного целочисленного массива A . Реализован подсчет числа парных чисел массива.
// инициализация одномерного массива A int[] A = < 1, 8, 3, 6, 5, 10, 31 , 24, 10, 13, 2, 4 >; int k = 0; // количество парных чисел // расширенный цикл for for (int d : A) if (d % 2 == 0) k++; // k = 7
2.2. Пример. Обработка массива объектов типа Book (книга)
Пусть задан класс Book , реализующий книгу
// класс, реализующий книгу class Book < String title; // название книги String author; // имя автора float price; // цена int year; // год издания >
Использование расширенного цикла for для массива типа Book может быть, например, следующим:
// объявление одномерного массива типа Book Book B[]; // выделить память для массива из 4 ссылок на тип Book B = new Book[4]; // выделить память для каждого элемента массива типа Book for (int i=0; ilength; i++) B[i] = new Book(); // заполнить массив значениями B[0].title = "Book-1"; B[0].author = "Author-1"; B[0].price = 205.78f; B[0].year = 2008; B[1].title = "Book-2"; B[1].author = "Author-2"; B[1].price = 99.00f; B[1].year = 2010; B[2].title = "Book-3"; B[2].author = "Author-3"; B[2].price = 0.99f; B[2].year = 2011; B[3].title = "Book-4"; B[3].author = "Author-4"; B[3].price = 100.01f; B[3].year = 2012; // расширенный цикл for // поиск книг 2011, 2012 годов for (Book book : B) if ((book.year==2011)||(book.year==2012)) System.out.println("Book: " + book.title + ", " + book.author);
В результате выполнения вышеприведенного кода будут выведены строки
Book: Book-3, Author-3 Book: Book-4, Author-4
3. Примеры обработки коллекций расширенным циклом for
3.1. Пример. Обработка коллекции типа Integer с помощью динамического массива ArrayList
В примере демонстрируется обработка коллекции целых чисел с помощью классов ArrayList и Integer .
Класс ArrayList реализует динамический массив, который может увеличиваться или уменьшаться по мере необходимости. Чтобы использовать данный класс нужно подключить пакет java.util . Объявление класса ArrayList следующее:
class ArrayList
Класс Integer есть классом-оберткой над типом int , который представляет целочисленные значения. В данном примере класс ArrayList для обработки объектов типа Integer имеет вид:
class ArrayList
Демонстрация использования расширенного цикла for для коллекции целых чисел с помощью класса ArrayList может быть, например, такой
// подключить пакет java.util import java.util.*; . // класс ArrayList, объект типа коллекция Integer // Integer - класс-обертка для типа int ArrayList A = new ArrayList(); // создать коллекцию // добавить элементы в коллекцию A.add(5); A.add(9); A.add(-20); A.add(11); A.add(7); // A = // найти сумму элементов коллекции int sum = 0; for (Integer i : A) // расширенный цикл for sum += i; System.out.println("Sum color: #008000;">// Sum = 12
3.2. Обработка коллекции типа Book с помощью связного списка LinkedList
Пусть задан класс (тип) Book , который имеет следующее объявление:
// класс, который реализует книгу class Book < String title; // название книги String author; // имя автора float price; // цена int year; // год издания >
В данном примере для демонстрации расширенного цикла for используется класс LinkedList . Этот класс предназначен для организации данных заданного типа в связный список. Чтобы использовать класс LinkedList в программе нужно подключить пакет java.util . Объявление класса имеет вид:
class LinkedList
Программный код, который формирует список книг типа Book и обрабатывает его в расширенном цикле for (поиск заданной книги) имеет вид:
// подключить пакет java.util import java.util.*; . // объявить и создать объект типа LinkedList LinkedList LB = new LinkedList(); Book B = new Book(); // дополнительная переменная // сформировать список объектов LinkedList // сформировать объект типа Book B.title = "Title-1"; B.author = "Author-1"; B.price = 10.00f; B.year = 2000; // добавить объект B в список LB.add(B); // добавить второй объект B = new Book(); // выделить память B.title = "Title-2"; B.author = "Author-2"; B.price = 20.00f; B.year = 2001; LB.add(B); // добавить третий объект B = new Book(); // выделить память B.title = "Title-3"; B.author = "Author-3"; B.price = 30.00f; B.year = 2002; LB.add(B); // вывести список LB int i=0; for (Book B2 : LB) < i++; System.out.println("Book - " + i + ": " + B2.title + ", " + B2.author); > System.out.println("-----------------------------------"); // вывести книги 2001 года выпуска for (Book B2 : LB) if (B2.year == 2001) System.out.println(B2.title + ", " + B2.author + ", " + B2.year);
В вышеприведенном коде в строках
int i=0; for (Book B2 : LB) < i++; System.out.println("Book - " + i + ": " + B2.title + ", " + B2.author); >
номер позиции в связном списке Book вычисляется путем введения дополнительной переменной i . Бывают случаи, когда нужно обрабатывать не все элементы коллекции. По значению итератора i можно определять номер позиции объекта коллекции, который может быть обработан.
После выполнения вышеприведенного кода на экран будет выведено:
Book - 1: Title-1, Author-1 Book - 2: Title-2, Author-2 Book - 3: Title-3, Author-3 ----------------------------------- Title-2, Author-2, 2001
4. Какие преимущества и недостатки применения расширенного цикла for для массивов
Расширенный цикл for есть удобным для обработки коллекций. Использование расширенного цикла for дает следующие преимущества:
- упрощенность и рациональность представления в сравнении с циклом for ;
- не нужно использовать дополнительную переменную цикла, задавать ее начальное значение и условие завершения цикла;
- не нужно индексировать массив.
Основной недостаток расширенного цикла for :
- отсутствие «гибкости» в оперировании итерационной переменной в случаях, когда нужно осуществлять перебор не всех элементов коллекции. Например, если нужно перебирать только первые n элементов всей коллекции или элементы, которые лежат на определенных позициях (четных позициях, нечетных позициях) в коллекции. Однако, этот недостаток можно обойти введением дополнительных переменных-итераторов и проверки соответствующих условий.
Связанные темы
The For-Each Loop
Iterating over a collection is uglier than it needs to be. Consider the following method, which takes a collection of timer tasks and cancels them:
void cancelAll(Collection c) < for (Iterator i = c.iterator(); i.hasNext(); ) i.next().cancel(); >
The iterator is just clutter. Furthermore, it is an opportunity for error. The iterator variable occurs three times in each loop: that is two chances to get it wrong. The for-each construct gets rid of the clutter and the opportunity for error. Here is how the example looks with the for-each construct:
void cancelAll(Collection c) < for (TimerTask t : c) t.cancel(); >
When you see the colon ( : ) read it as «in.» The loop above reads as «for each TimerTask t in c .» As you can see, the for-each construct combines beautifully with generics. It preserves all of the type safety, while removing the remaining clutter. Because you don’t have to declare the iterator, you don’t have to provide a generic declaration for it. (The compiler does this for you behind your back, but you need not concern yourself with it.)
Here is a common mistake people make when they are trying to do nested iteration over two collections:
List suits = . ; List ranks = . ; List sortedDeck = new ArrayList(); // BROKEN - throws NoSuchElementException! for (Iterator i = suits.iterator(); i.hasNext(); ) for (Iterator j = ranks.iterator(); j.hasNext(); ) sortedDeck.add(new Card(i.next(), j.next()));
Can you spot the bug? Don’t feel bad if you can’t. Many expert programmers have made this mistake at one time or another. The problem is that the next method is being called too many times on the «outer» collection ( suits ). It is being called in the inner loop for both the outer and inner collections, which is wrong. In order to fix it, you have to add a variable in the scope of the outer loop to hold the suit:
// Fixed, though a bit ugly for (Iterator i = suits.iterator(); i.hasNext(); ) < Suit suit = (Suit) i.next(); for (Iterator j = ranks.iterator(); j.hasNext(); ) sortedDeck.add(new Card(suit, j.next())); >
So what does all this have to do with the for-each construct? It is tailor-made for nested iteration! Feast your eyes:
for (Suit suit : suits) for (Rank rank : ranks) sortedDeck.add(new Card(suit, rank));
The for-each construct is also applicable to arrays, where it hides the index variable rather than the iterator. The following method returns the sum of the values in an int array:
>// Returns the sum of the elements of a> int sum(int[] a) < int result = 0; for (int i : a) result += i; return result; >
So when should you use the for-each loop? Any time you can. It really beautifies your code. Unfortunately, you cannot use it everywhere. Consider, for example, the expurgate method. The program needs access to the iterator in order to remove the current element. The for-each loop hides the iterator, so you cannot call remove . Therefore, the for-each loop is not usable for filtering. Similarly it is not usable for loops where you need to replace elements in a list or array as you traverse it. Finally, it is not usable for loops that must iterate over multiple collections in parallel. These shortcomings were known by the designers, who made a conscious decision to go with a clean, simple construct that would cover the great majority of cases.