52

52


}
} /* Output-
0 Rat 1 Manx 2 Cymric 3.Mutt 4 Pug 5.Cymric 6 Pug 7 Manx 4:Pug 6 Pug 3 Mutt 1 Manx 5 Cymric 7 Manx 2:Cymric O-Rat O-Rat 1 Manx 2-Cymric 3-Mutt 4-Pug 5 Cymric 6 Pug 7 Manx 4-Pug 6 Pug 3 Mutt 1 Manx 5:Cymric 7.Manx 2 Cymric 0:Rat
{Ralph=Rat. Eric=Manx, Robin=Cymric. Lacey=Mutt. Britney=Pug. Sam=Cymric. Spot=Pug. Fluffy=Manx}
[Ralph. Eric. Robin. Lacey. Britney. Sam. Spot. Fluffy] 0:Rat l.Manx 2-Cymric 3-Mutt 4:Pug 5-Cymric 6:Pug 7 Manx 0:Rat 1 Manx 2-Cymric 3-Mutt 4.Pug 5:Cymric 6:Pug 7 Manx */// ~
Обе версии display() работают как с объектами Map, так и с подтипами Collection; при этом как Collection, так и Iterator изолируют методы display() от знания конкретной реализации используемого контейнера.
В данном случае два решения примерно равноценны. Использование Iterator становится предпочтительным при реализации постороннего класса, для которого реализация интерфейса Collection затруднена или нежелательна. Например, если мы создаем реализацию Collection наследованием от класса, со­держащего объекты Pet, нам придется реализовать все методы Collection, даже если они не будут использоваться в методе display(). Хотя проблема легко ре­шается наследованием от AbstractCollection, вам все равно придется реализо­вать iterator() вместе с size(), чтобы предоставить методы, не реализованные AbstractCollection, но используемые другими методами AbstractCollection:
// • hoidi ng/Col1ecti onSequence.java import typeinfo pets.*; import java.util.*;
public class CollectionSequence extends AbstractCollection<Pet> {
private Pet[] pets = Pets.createArray(8); public int sizeO { return pets.length; } public Iterator<Pet> iteratorO {
return new Iterator<Pet>() {
private int index = 0; public boolean hasNextO. {
return index < pets.length;
public Pet nextО { return pets[index++]; } public void removeО { // He реализован
throw new UnsupportedOperationExceptionO;
}
}:
}
public static void main(String[] args) {
CollectionSequence с = new Col 1ectionSequence(); InterfaceVsIterator.di splay(с); InterfaceVsIterator.di splay(c.i terator());
}
} /* Output:
0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx *///:-
Метод remove() являетсянеобязательной операцией. В нашем примере реа- лизовывать его не нужно, и в случае вызова он выдает исключение.
Из приведенного примера видно, что при реализации Collection вы также реализуете iterator(), а простая отдельная реализация iterator() требует чуть меньших усилий, чем наследование от AbstractCollection. Но, если класс уже на­следует от другого класса, наследование еще и от AbstractCollection невозможно. В этом случае для реализации Collection придется реализовать все методы ин­терфейса, и тогда гораздо проще ограничиться наследованием и добавить воз­можность создания итератора:
//: hoidi ng/NonCol1ecti onSequence.java import typeinfo.pets.*; import java.util.*;
class PetSequence {
protected Pet[] pets = Pets.createArray(8);
}
public class NonCollectionSequence extends PetSequence { public Iterator<Pet> iteratorO {
return new Iterator<Pet>() {
private int index = 0; public boolean hasNextO {
return index < pets length;
}
public Pet nextO { return pets[index++]; } public void removeO { // He реализован
throw new UnsupportedOperationExceptionO;
}
}:
}
public static void main(String[] args) {
NonCollectionSequence nc = new NonCollectionSequence(); InterfaceVsIterator.display(nc.iteratorO);
}
} /* Output:
0:Rat l:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx *///:-

Создание Iterator обеспечивает минимальную логическую привязку между последовательностью и методом, использующим эту последовательность, а так­же налагает гораздо меньше ограничений на класс последовательности, реали­зующий Collection.
Синтаксис foreach и итераторы
До настоящего момента «синтаксис foreach» использовался в основном с масси­вами, но он также будет работать с любым объектом Collection. Некоторые при­меры уже встречались нам при работе с ArrayList, но можно привести и более об­щее подтверждение:
//: holding/ForEachCollections java
// Синтаксис foreach работает с любыми коллекциями
import java.util.*,
public class ForEachCollections {
public static void main(String[] args) {
Collection<String> cs = new LinkedList<String>(); Col lections.addAl1(cs,
"Take the long way home".splitC' ")); for(String s : cs)
System, out. pri nt(.. + s + ...... ),
}
} /* Output-
'Take' 'the' 'long' 'way' 'home' *///:-
Поскольку cs является Collection, этот пример показывает, что поддержка foreach является характеристикой всех объектов Collection.
Работа этой конструкции объясняется тем, что в Java SE5 появился новый интерфейс Iterable, который содержит метод iterator() для создания Iterator, и именно интерфейс Iterable используется при переборе последовательности в синтаксисе foreach. Следовательно, создав любой класс, реализующий Iterable, вы сможете использовать его в синтаксисе foreach:
//: hoidi ng/IterableClass.java // Anything Iterable works with foreach. import java.util.*;
public class IterableClass implements Iterable<String> { protected StringE] words = ("And that is how " +
"we know the Earth to be banana-shaped.").splitC "); public Iterator<String> iteratorO {
return new Iterator<String>() { private int index = 0; public boolean hasNextO {
return index < words length;
}
public String nextO { return words[index++]; } public void remove0 { // Not implemented
throw new UnsupportedOperationExceptionO,
};
public static void main(Stnng[] args) {
for(String s • new IterableClassO) System out print(s + " ");
}
} /* Output.
And that is how we know the Earth to be banana-shaped. *///:-
Метод iterator() возвращает экземпляр анонимной внутренней реализации Iterator<string>, последовательно доставляющей каждое слово в массиве. В main() мы видим, что IterableClass действительно работает в синтаксисе foreach.
В Java SE5 многие классы реализуют Iterable, прежде всего все классы Collection (но не Map). Например, следующий код выводит все переменные окру­жения (environment) операционной системы:
//: holding/Envi ronmentVariables.java import java util *;
public class EnvironmentVariables {
public static void main(String[] args) {
for (Map Entry entry System getenvO .entrySetO) { System.out.println(entry.getKey() + ": " + entry. getValueO);
}
}
} /* (Выполните, чтобы увидеть результат) *///:-
System.getenv() возвращает Map, entrySet() создает Set с элементами Map.Entry, a Set поддерживает Iterable и поэтому может использоваться в цикле foreach.
Синтаксис foreach работает с массивами и всем, что поддерживает Iterable, но это не означает, что массив автоматически поддерживает Iterable:
// ■ hoiding/ArraylsNotIterable.java import java.util.*;
public class ArraylsNotlterable {
static <T> void test(Iterable<T> ib) { for(T t • ib)
System.out.print(t + " ");
}
public static void main(String[] args) { test(Arrays.asList(l. 2, 3)); StringC] strings = { "А", "В". "С" }: // Массив работает в foreach, но не является Iterable: //! test(strings);
// его необходимо явно преобразовать к Iterable: testCArrays.asLi st(stri ngs));
}
} /* Output: 1 2 3 А В С *///•-
Попытка передачи массива в аргументе Iterable завершается неудачей. Авто­матическое преобразование в Iterable не производится; его необходимо выпол­нять вручную.
Идиома «метод-адаптер»
Что делать, если у вас имеется существующий класс, реализующий Iterable, и вы хотите добавить новые способы использования этого класса в синтаксисе foreach? Допустим, вы хотите иметь возможность выбора между перебором спи­ска слов в прямом или обратном направлении. Если просто воспользоваться на­следованием от класса и переопределить метод iterator, то существующий метод будет заменен и никакого выбора не будет.
Одно из решений этой проблемы основано на использовании идиомы, кото­рую я называю «методом-адаптером». Термин «адаптер» происходит от одно­именного паттерна: вы должны предоставить интерфейс, необходимый для ра­боты синтаксиса foreach. Если у вас имеется один интерфейс, а нужен другой, проблема решается написанием адаптера. В данном случае требуетсядобавитьк стандартному «прямому» итератору обратный, так что переопределение ис­ключено. Вместо этого мы добавим метод, создающий объект Iterable, который может использоваться в синтаксисе foreach. Как будет показано далее, это по­зволит нам предоставить несколько вариантов использования foreach:
//: hoiding/AdapterMethodldiom.java
// Идиома "метод-адаптер" позволяет использовать foreach
// с дополнительными разновидностями Iterable.
import java.util.*;
class ReversibleArrayList<T> extends ArrayList<T> {
public ReversibleArrayList(Collection<T> c) { super(c); }. public Iterable<T> reversedO {
return new Iterable<T>() {
public Iterator<T> iteratorO {
return new Iterator<T>() {
int current = sizeO - 1,
public boolean hasNextO { return current > -1;
}
public T nextO { return get (current--); } public void removeO { // He реализован throw new
UnsupportedOperationExceptionO;
}
} •
}
}:
}
}
public class AdapterMethodldiom {
public static void main(String[] args) { ReversibleArrayList<String> ral =
new ReversibleArrayList<String>(
Arrays.asList(To be or not to be".splitC' "))): // Получаем обычный итератор, полученный при помощи iteratorO: forCString s : ral)
System.out.print(s + " "); System.out printlnO;

// Передаем выбранный нами Iterable forCString s • ral .reversedO)
System.out.print(s + " "),
}
} /* Output To be or not to be be to not or be To */// ~
Если просто поместить объект ral в синтаксис foreach, мы получим (стан­дартный) «прямой» итератор. Но если вызвать для объекта reversed(), поведе­ние изменится.
Использовав этот прием, можно добавить в пример IterableClass.java два ме­тода-адаптера:
// hoidi ng/MultiIterableClass.java // Adding several Adapter Methods, import java util *;
public class MultilterableClass extends IterableClass { public Iterable<String> reversedO {
return new Iterable<String>() {
public Iterator<String> iteratorO {
return new Iterator<String>() {
int current = words length - 1,
public boolean hasNextO { return current > -1;
}
public String nextO { return words[current--];
}
public void removeО { // He реализован throw new
UnsupportedOperationException(),
}
}:
}
}.
}
public Iterable<String> randomizedO { return new Iterable<String>() {
public Iterator<String> iteratorO { List<String> shuffled =
new ArrayList<String>(Arrays.asList(words)); Collections.shuffleCshuffled, new Random(47)); return shuffled.iterator();
}
}:
}
public static void main(String[] args) {
MultilterableClass mic = new MultiIterableClassO; for (String s : mic. reversedO)
System out print(s + " "): System, out. pri ntlnO. for(String s : mic.randomizedO)
System out.print(s + " "); System.out.prmtlnO:продолжение &for(String s : mic)
System.out.print(s + " ");
}
} /* Output:
banana-shaped, be to Earth the know we how is that And is banana-shaped. Earth that how the be And we know to And that is how we know the Earth to be banana-shaped *///:-
Из выходных данных видно, что метод Collections.shuffle не изменяет исход­ный массив, а только переставляет ссылки в shuffled. Так происходит только по­тому, что метод randomized() создает для результата Arrays.asList() «обертку» в виде ArrayList. Если бы операция выполнялась непосредственно с объектом List, полученным от Arrays.asList(), то это привело бы к изменению нижележаще­го массива: