Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 57 58 59 60 61 62 63 64 65 ... 132

• Удалить из последовательности последний элемент, возвращенный итератором, методом remove().

Чтобы увидеть итератор в действии, мы снова воспользуемся иерархией Pets:

// holding/Simplelteration java import typeinfo pets *; import java util *.

public class Simplelteration {

public static void main(String[] args) {

List<Pet> pets = Pets arrayList(12); Iterator<Pet> it = pets iteratorO. whi 1 e(it hasNextO) {

Pet p = it nextO;

System.out pri nt(p id() + " " + p + " ");

}

System.out printlnO; // Более простой способ, for(Pet p • pets)

System out print(p id() + "+ p + " "); System, out. printlnO;

// Итератор также способен удалять элементы: it = pets. iteratorO. for(int i = 0: i < 6: i++) { it nextO: it.removeO.

}

System.out.pnntln(pets):

}

} /* Output-

0:Rat l:Manx 2:Cymric 3-Mutt 4-Pug 5:Cymric 6.Pug 7:Manx 8.Cymric 9:Rat 10:EgyptianMau 11.Hamster

0-Rat 1-Manx 2-Cymric 3:Mutt 4:Pug 5.Cymric 6:Pug 7:Manx 8:Cymric 9-Rat 10-EgyptianMau 11:Hamster

[Pug. Manx. Cymric. Rat. EgyptianMau. Hamster] *///•-

Мы видим, что с Iterator можно не беспокоиться о количестве элементов в последовательности. Проверка осуществляется методами hasNext() и next().

Если вы просто перебираете элементы списка в одном направлении, не пытаясь модифицировать его содержимое, «синтаксис foreach» обеспечивает более компактную запись.

Iterator удаляет последний элемент, полученный при помощи next(), поэтому перед вызовом remove() необходимо вызвать next().

Теперь рассмотрим задачу создания метода display(), не зависящего от типа контейнера:

//: hoiding/CrossContaiпегIteration.java import typeinfo.pets.*, import java.util *;

public class CrossContainerlteration {

public static void display(Iterator<Pet> it) { while(it.hasNextO) {

Pet p = it nextO:

System.out.print(p.id() + ":" + p + " ");

}

System out.printlnO;

}

public static void main(String[] args) { продолжение &

ArrayList<Pet> pets = Pets.arrayList(8); LinkedList<Pet> petsLL = new LinkedList<Pet>(pets); HashSet<Pet> petsHS = new HashSet<Pet>(pets); TreeSet<Pet> petsTS = new TreeSet<Pet>(pets); di splay(pets.iterator()); display(petsLL iteratorO); di splay(petsHS.iterator()); di spl ay(petsTS. iteratorO);

}

} /* Output:

0:Rat 1-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 4:Pug 6:Pug 3:Mutt l:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat 5 Cymric 2:Cymric 7:Manx l:Manx 3:Mutt 6:Pug 4:Pug 0:Rat *///:-

В методе display() отсутствует информация о типе последовательности, и в этом проявляется истинная мощь итераторов: операция перемещения по последовательности отделяется от фактической структуры этой последовательности. Иногда говорят, что итераторы унифицируют доступ к контейнерам.

Listlterator

Listlterator — более мощная разновидность Iterator, поддерживаемая только классами List. Если Iterator поддерживает перемещение только вперед, List-Iterator является двусторонним. Кроме того, он может выдавать индексы следующего и предыдущего элементов по отношению к текущей позиции итератора в списке и заменять последний посещенный элемент методом set(). Вызов listIterator() возвращает Listlterator, указывающий в начало List, а для создания итератора Listlterator, изначально установленного на элемент с индексом п, используется вызов listlterator(n). Все перечисленные возможности продемонстрированы в следующем примере:

//: hoiding/Listlteration java import typeinfo.pets.*, import java.util *;

public class Listlteration {

public static void main(String[] args) {

List<Pet> pets = Pets.arrayList(8); ListIterator<Pet> it = pets.listlteratorO; while(it.hasNextO)

System.out.print(it.next0 + " + it.nextlndexO + " + it.previousIndexO + ");

System.out.printi n(). // В обратном направлении: while(it.hasPreviousO)

System.out.print(it.previousO.id() + " "); System, out pri nti nO. System.out.printin(pets): it = pets.listlteratorO): while(it.hasNextO) { it.nextO:

i t.set(Pets.randomPet 0):

System out println(pets).

}

} /* Output.

Rat. 1. 0; Manx. 2. 1. Cymric. 3. 2; Mutt. 4. 3. Pug. 5. 4. Cymric. 6. 5. Pug. 7. 6. Manx. 8. 7. 7 6 5 4 3 2 1 0

[Rat. Manx. Cymric. Mutt. Pug. Cymric. Pug. Manx] [Rat. Manx. Cymric. Cymric. Rat. EgyptianMau. Hamster. EgyptianMau] *///•-

Метод Pets.randomPet() используется для замены всех объектов Pet в списке, начиная с позиции 3 и далее.

LinkedList

LinkedList тоже реализует базовый интерфейс List, как и ArrayList, но выполняет некоторые операции (например, вставку и удаление в середине списка) более эффективно, чем ArrayList. И наоборот, операции произвольного доступа выполняются им с меньшей эффективностью.

Класс LinkedList также содержит методы, позволяющие использовать его в качестве стека, очереди (Queue) или двусторонней очереди (дека).

Некоторые из этих методов являются псевдонимами или модификациями для получения имен, более знакомых в контексте некоторого использования. Например, методы getFirst() и element() идентичны — они возвращают начало (первый элемент) списка без его удаления и выдают исключение NoSuch-ElementException для пустого списка. Метод реек() представляет собой небольшую модификацию этих двух методов: он возвращает null для пустого списка.

Метод addFirst() вставляет элемент в начало списка. Метод offer() делает то же, что add() и addLast() — он добавляет элемент в конец списка. Метод removeLast() удаляет и возвращает последний элемент списка.

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

// hoiding/LinkedListFeatures java import typeinfo pets *; import java util *;

import static net.mindview util Print.*;

public class LinkedListFeatures {

public static void main(String[] args) { LinkedList<Pet> pets =

new LinkedList<Pet>(Pets arrayList(5)); print(pets); // Идентично

print("pets.getFirst()• " + pets getFirstO).

print ("pets element О " + pets.elementO);

// Различие проявляется только для пустых списков:

print("pets peekO: " + pets.peekO);

// Идентично, удаление и возврат первого элемента.

print("pets removeO: " + pets.removeO);

print ("pets removeFirstO: " + pets.removeFirstO);

// Различие проявляется только для пустых списков: продолжение

print ("pets pollO " + pets poll О). print(pets).

pets addFirst(new RatO).

print("After addFirstO " + pets).

pets offer(Pets randomPetO).

print("After offer() " + pets).

pets.add(Pets randomPetO).

print ("After addO " + pets).

pets addLast(new HamsterO).

print ("After addLastO " + pets).

print ("pets removeLastO " + pets removeLastO).

}

} /* Output

[Rat, Manx. Cymric. Mutt. Pug]

pets getFirstO. Rat

pets elementO- Rat

pets.peek О Rat

pets.removeО: Rat

pets removeFirstO: Manx

pets.poll О Cymric

[Mutt. Pug]

After addFirstO: [Rat. Mutt. Pug] After offerO: [Rat. Mutt. Pug. Cymric] After addO: [Rat. Mutt. Pug. Cymric. Pug] After addLastO* [Rat. Mutt. Pug. Cymric. Pug. Hamster] pets removeLastO. Hamster *///:-

Результат Pets.arrayList() передается конструктору LinkedList для заполнения. Присмотревшись к интерфейсу Queue, вы найдете в нем методы element(), offer(), peek(), poll() и remove(), добавленные в LinkedList для использования в реализации очереди (см. далее).

Стек

Стек часто называют контейнером, работающим по принципу «первым вошел, последним вышел» (LIFO). То есть элемент, последним занесенный в стек, будет первым, полученным при извлечении из стека.

В классе LinkedList имеются методы, напрямую реализующие функциональность стека, поэтому вы просто используете LinkedList, не создавая для стека новый класс. Впрочем, иногда отдельный класс для контейнера-стека лучше справляется с задачей:

//. net/mi ndvi ew/uti1/Stack java // Создание стека из списка LinkedList. package net.mindview.util: import java.util.LinkedList:

public class Stack<T> {

private LinkedList<T> storage = new LinkedList<T>(); public void push(T v) { storage.addFirst(v); } public T peek О { return storage. getFirstO: } public T popO { return storage removeFirstO: } public boolean emptyО { return storage.isEmptyО: }

public String toStringO { return storage.toStringO. } } ///:-

Это простейший пример определения класса с использованием параметризации. Суффикс <Т> после имени класса сообщает компилятору, что тип является параметризованным по типу Т — при использовании класса на место Т будет подставлен фактический тип. Фактически такое определение означает: «Мы определяем класс Stack для хранения объектов типа Т». Stack реализуется на базе LinkedList, также предназначенного для хранения типа Т. Обратите внимание: метод push() получает объект типа Т, а методы реек() и рор() возвращают объект типа Т. Метод реек() возвращает верхний элемент без извлечения из стека, а метод рор() удаляет и возвращает верхний элемент. Простой пример использования нового класса Stack:

//• hoiding/StackTest.java import net.mindview util.*:

public class StackTest {

public static void main(String[] args) {

Stack<String> stack = new Stack<String>(): for(String s • "My dog has fleas" .splitC' "))

stack.push(s); whi led stack. emptyO)

System out pri nt(stack.pop() + " ");

}

} /* Output: fleas has dog My *///:-

Если вы хотите использовать класс Stack в своем коде, вам придется либо полностью указать пакет, либо изменить имя класса при создании объекта; в противном случае, скорее всего, возникнет конфликт с классом Stack из пакета java.util. Пример использования имен пакетов при импортировании java. util.* в предыдущем примере:

//: holding/StackCollision.java import net.mindview.util.*:

public class StackCol1ision {

public static void main(String[] args) {

net.mindview.util.Stack<String> stack =

new net.mindview.util.Stack<String>(); for(String s : "My dog has fleas".splitC "))

stack.push(s): whi led stack, empty О)

System, out. pri nt (stack. popO + " "); System, out. printlnO; java.util.Stack<String> stack2 =

new java.util.Stack<String>(): for(String s : "My dog has fleas".splitC' "))

stack2.push(s); while( !stack2 emptyO)

System.out print(stack2.pop() + " ").

}

fleas has dog My fleas has dog My *///:-

В java.util нет общего интерфейса Stack — вероятно, из-за того, что имя было задействовано в исходной, неудачно спроектированной версии java. util.Stack для Java 1.0. Хотя класс java.util.Stack существует, LinkedList обеспечивает более качественную реализацию стека, и решение net.mindview.util. Stack является предпочтительным.

Множество

В множествах (Set) каждое значение может храниться только в одном экземпляре. Попытки добавить новый экземпляр эквивалентного объекта блокируются. Множества часто используются для проверки принадлежности, чтобы вы могли легко проверить, принадлежит ли объект заданному множеству. Следовательно, важнейшей операцией Set является операция поиска, поэтому на практике обычно выбирается реализация HashSet, оптимизированная для быстрого поиска.

1 ... 57 58 59 60 61 62 63 64 65 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий