Java编程思想学习笔记(11)
持有对象
通常,程序总是根据运行时才知道的某些条件去创建新对象,在此之前,不会知道所需对象的数量,甚至不知道确切的类型。为解决这个普遍的问题,需要在任意时刻和任意位置创建任意数量的对象,所以就不能依靠创建命名的引用来持有每一个对象:
如 MyType aReference;
因为你实际上不知道需要多少个这样的引用。
大多数语言都提供某种方法来解决这个基本问题。Java有多种方式保存对象,例如数组,但是数组具有固定的尺寸,而在更一般的情况下,你在写程序时并不知道需要多少个对象,或者是否需要更加复杂的方式来存储对象,因此数组尺寸固定这一限制就显得过于受限了。
Java提供了一套相当完整的容器类来解决这个问题。其中基本的类型是List,Set,Queue,Map。
这些对象被称为集合类。
泛型和类型安全的容器
用Java SE5之前的容器的一个主要问题就是编译器允许你向容器插入不正确的类型。
class Apple {
private static long counter;
private final long id = counter++;
public long id() { return id; }
}
class Orange {}
public class ApplesAndOrangesWithoutGenerics {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for(int i = 0; i < 3; i++)
apples.add(new Apple());
// Not prevented from adding an Orange to apples:
apples.add(new Orange());
for(int i = 0; i < apples.size(); i++)
((Apple)apples.get(i)).id();
// Orange is detected only at run time
}
}
上边,Apple和Orange都放在了容器中,然后将它们取出来。正常情况下,Java编译器会报告警告信息,因为没有使用泛型,这里使用@SuppressWarnings注解及其参数表示只有有关“不受检查的异常”的警告信息应该被抑制。
因为ArrayList保存的是Objects对象,所以可以通过add方法将Apple对象放进容器中,还可以添加Orange对象,而且无论是在编译期还是运行时都没有问题。
当你用get方法取出来你觉得是Apple的对象时,你得到的只是Object的引用,必须将其转型为Apple。否则你就会得到语法错误。
要想定义用来保存Apple对象的ArrayList,可以声明ArrayList
public class ApplesAndOrangesWithGenerics {
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for(int i = 0; i < 3; i++)
apples.add(new Apple());
// Compile-time error:
// apples.add(new Orange());
for(int i = 0; i < apples.size(); i++)
System.out.println(apples.get(i).id());
// Using foreach:
for(Apple c : apples)
System.out.println(c.id());
}
}
当你指定了某个类型作为泛型参数时,你不仅可以将该确切类型的对象放置到容器中,向上转型也可以像作用于其它类型那样作用于泛型。
public abstract class People {
abstract void where();
}
public class Chinese extends People{
private String string;
public Chinese() {
this.string = "Chinese";
}
@Override
void where() {
System.out.println(string);
}
}
public class Janpanse extends People{
private String string;
public Janpanse() {
this.string = "Janpanse";
}
@Override
void where() {
System.out.println(string);
}
}
public class GenericsAndUpcasting {
public static void main(String[] args) {
ArrayList peoples = new ArrayList<>();
peoples.add(new Chinese());
peoples.add(new Janpanse());
for (People people : peoples) {
people.where();
}
}
}
基本概念
Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
1 Collection 一个独立元素的序列,这些元素都服从一条或多条规则
1.1 List必须按照插入顺序保存元素
1.2 Set不能有重复元素
1.3 Queue按照队列规则来确定对象产生的顺序
2 Map。一组键值对对象。
你应该创建一个具体类的对象,然后将其转型为对应的接口,然后在其余的代码中都使用这个接口。
Collection接口概括了序列的概念—————— 一种存放一组对象的方式。
public class SimpleCollection {
public static void main(String[] args) {
Collection c = new ArrayList();
for(int i = 0; i < 10; i++)
c.add(i); // Autoboxing
for(Integer i : c)
System.out.print(i + ", ");
}
}
上面例子只使用了Collection,任何继承自Collection类的对象都可以正常工作。
添加一组元素
Arrays.asList()方法接受一个数组或是一个用逗号分隔的元素列表(使用可变参数),并将其转换为一个List对象。Collections.addAll( ) 方法接受一个Collection对象,以及一个数组或是一个用逗号分割的列表,将元素添加到Collection中。
public class AddingGroups {
public static void main(String[] args) {
Collection collection = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
Integer[] moreInts = { 6, 7, 8, 9, 10 };
collection.addAll(Arrays.asList(moreInts));
// Runs significantly faster, but you can’t
// construct a Collection this way:
Collections.addAll(collection, 11, 12, 13, 14, 15);
Collections.addAll(collection, moreInts);
// Produces a list "backed by" an array:
List list = Arrays.asList(16, 17, 18, 19, 20);
list.set(1, 99); // OK -- modify an element
// Exception in thread "main" java.lang.UnsupportedOperationException
// list.add(21); // Runtime error because the
// underlying array cannot be resized.
}
}
Collection的构造器可以接受另一个Collection,用来将自身初始化。因此你可以使用
Arrays.asList()来为这个构造器产生输入。
但是Collections.addAll() 方法运行的要快很多,而且构建一个不包含元素的Collection,然后调用Collections.addAll()这种方式是很方便的。
Collection.addAll()方法只能接受另一个Collection对象作为参数,因此它不如Arrays.asList() 或者Collections.addAll()灵活。这两个方法使用的都是可变参数列表。
也可以直接使用Arrays.asList()的输出,将其当做List,但是在这种情况下,其底层表示是数组,因此不能调整尺寸。
class Car {
}
public class CarUseGas extends Car{
}
public class CarUseElec extends Car{
}
public class Audi extends CarUseGas{
}
public class TesLa extends CarUseElec{
}
public class BMW extends CarUseGas{
}
public class AsListInference {
public static void main(String[] args) {
List c1 = Arrays.asList(
new CarUseElec(),new CarUseGas()
);
// 不会编译
List c2 = Arrays.asList(
new Audi(),new BMW()
);
List c3 = new ArrayList();
Collections.addAll(c3,new Audi(),new TesLa());
List c4 = Arrays.asList(
new Audi(),new TesLa()
);
// System.out.println(c2.get(0).getClass());
}
}
当创建c2时,Arrays.asList()只有 CarUseGas类型,因此它会创建List< CarUseGas >而不是List< Car >.
容器的打印
public class PrintingContainers {
static Collection fill(Collection collection) {
collection.add("rat");
collection.add("cat");
collection.add("dog");
collection.add("dog");
return collection;
}
static Map fill(Map map) {
map.put("rat", "Fuzzy");
map.put("cat", "Rags");
map.put("dog", "Bosco");
map.put("dog", "Spot");
return map;
}
public static void main(String[] args) {
print(fill(new ArrayList()));
print(fill(new LinkedList()));
print(fill(new HashSet()));
print(fill(new TreeSet()));
print(fill(new LinkedHashSet()));
print(fill(new HashMap()));
print(fill(new TreeMap()));
print(fill(new LinkedHashMap()));
}
}
上面展示了Java容器类库中的两种主要类型,区别在于容器中每个“槽”保存的元素个数。
Collection每个槽中只能保存一个元素,Map在每个槽内保存两个元素。
List
先贴上将要使用的Pet类及其子类以及一些方法:
public class Individual {
private static long counter = 0;
private final long id = counter++;
private String name;
public long id(){
return id;
}
public Individual(String name) {
this.name = name;
}
public Individual() {
}
@Override
public String toString() {
return getClass().getSimpleName() +
(name == null ? "" : " " + name);
}
}
public class Person extends Individual{
public Person(String name) {
super(name);
}
public Person() {
}
}
public class Pet extends Individual{
public Pet(String name) {
super(name);
}
public Pet() {
}
}
public class Cat extends Pet{
public Cat(String name) {
super(name);
}
public Cat() {
}
}
public class Dog extends Pet{
public Dog(String name) {
super(name);
}
public Dog() {
}
}
public class Rodent extends Pet{
// 啮齿动物
public Rodent(String name) {
super(name);
}
public Rodent() {
}
}
public class Manx extends Cat{
// 马恩岛猫
public Manx(String name) {
super(name);
}
public Manx() {
}
}
public class Cymric extends Manx{
// 威尔斯猫
public Cymric(String name) {
super(name);
}
public Cymric() {
}
}
public class EgyptianMau extends Cat{
// 埃及猫
public EgyptianMau(String name) {
super(name);
}
public EgyptianMau() {
}
}
public class Rat extends Rodent{
public Rat(String name) {
super(name);
}
public Rat() {
}
}
public class Hamster extends Rodent{
// 仓鼠
public Hamster(String name) {
super(name);
}
public Hamster() {
}
}
public class Pug extends Dog{
// 哈巴狗
public Pug(String name) {
super(name);
}
public Pug() {
}
}
public class Mouse extends Rodent{
public Mouse(String name) {
super(name);
}
public Mouse() {
}
}
public class Mutt extends Dog{
// 杂种狗
public Mutt(String name) {
super(name);
}
public Mutt() {
}
}
public abstract class PetCreator {
private Random random = new Random(47);
public abstract List> types();
public Pet randomPet(){
int n = random.nextInt(types().size());
try {
return types().get(n).newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e){
throw new RuntimeException(e);
}
}
public Pet[] createArray(int size){
Pet[] result = new Pet[size];
for (int i = 0; i < size ; i++) {
result[i] = randomPet();
}
return result;
}
public ArrayList arrayList(int size){
ArrayList result = new ArrayList<>();
Collections.addAll(result,createArray(size));
return result;
}
}
public class LiteralPetCreator extends PetCreator{
// No try block needed.
@SuppressWarnings("unchecked")
public static final List> allTypes =
Collections.unmodifiableList(Arrays.asList(
Pet.class, Dog.class, Cat.class, Rodent.class,
Mutt.class, Pug.class, EgyptianMau.class, Manx.class,
Cymric.class, Rat.class, Mouse.class,Hamster.class));
// Types for random creation:
private static final List> types =
allTypes.subList(allTypes.indexOf(Mutt.class),allTypes.size());
public List> types() {
return types;
}
}
public class Pets {
public static final PetCreator creator =
new LiteralPetCreator();
public static Pet randomPet() {
return creator.randomPet();
}
public static Pet[] createArray(int size) {
return creator.createArray(size);
}
public static ArrayList arrayList(int size) {
return creator.arrayList(size);
}
}
有两种类型的List:
ArrayList
LinkedList
例子:
public class ListFeatures {
public static void main(String[] args) {
Random rand = new Random(47);
List pets = Pets.arrayList(7);
print("1: " + pets);
Hamster h = new Hamster();
pets.add(h); // Automatically resizes
print("2: " + pets);
print("3: " + pets.contains(h));
pets.remove(h); // Remove by object
Pet p = pets.get(2);
print("4: " + p + " " + pets.indexOf(p));
Pet cymric = new Cymric();
print("5: " + pets.indexOf(cymric));
print("6: " + pets.remove(cymric));
// Must be the exact object:
print("7: " + pets.remove(p));
print("8: " + pets);
pets.add(3, new Mouse()); // Insert at an index
print("9: " + pets);
List sub = pets.subList(1, 4);
print("subList: " + sub);
print("10: " + pets.containsAll(sub));
// Collections.sort(sub); // In-place sort
print("sorted subList: " + sub);
// Order is not important in containsAll():
print("11: " + pets.containsAll(sub));
Collections.shuffle(sub, rand); // Mix it up
print("shuffled subList: " + sub);
print("12: " + pets.containsAll(sub));
List copy = new ArrayList(pets);
sub = Arrays.asList(pets.get(1), pets.get(4));
print("sub: " + sub);
copy.retainAll(sub);
print("13: " + copy);
copy = new ArrayList(pets); // Get a fresh copy
copy.remove(2); // Remove by index
print("14: " + copy);
copy.removeAll(sub); // Only removes exact objects
print("15: " + copy);
copy.set(1, new Mouse()); // Replace an element
print("16: " + copy);
copy.addAll(2, sub); // Insert a list in the middle
print("17: " + copy);
print("18: " + pets.isEmpty());
pets.clear(); // Remove all elements
print("19: " + pets);
print("20: " + pets.isEmpty());
pets.addAll(Pets.arrayList(4));
print("21: " + pets);
Object[] o = pets.toArray();
print("22: " + o[3]);
Pet[] pa = pets.toArray(new Pet[0]);
// print("23: " + pa[3].id());
}
// 第一行输出展示了最初的由Pet构成的List,与数组不同,List允许在它被创建之后添加元素,
// 移除元素,或者自我调整尺寸,
// 第二行,可以看到添加了一个Hamster
// 用contains方法来确定某个对象是否在列表中
// subList允许从较大的列表中创建出一个片断
// retainAll方法有效的交集操作
}
迭代器
使用容器,必须对容器的确切类型进行编程,这样并不会,比如你最开始是对着List编码的,但是后来发现如果能够把相同的代码应用于Set,将会显得更加方便,此时应该怎么做呢?或者打算从头开始编写通用的代码,它们只是使用容器,不知道或者说不关心容器的类型。
迭代器可以达到这一目的,迭代器是一个对象,它的工作是遍历并选择序列中的对象,而不必关系该序列底层的结构。
Java的Iterator只能单向移动,只能用来:
使用方法 iterator()要求容器返回一个Iterator,Iterator将会准备好返回序列的第一个元素
使用next()获得序列的下一个元素
使用hasNext()检查序列中是否还有元素
使用remove()将迭代器新近返回的元素删除
例子:
public class SimpleIteration {
public static void main(String[] args) {
List pets = Pets.arrayList(12);
Iterator it = pets.iterator();
while(it.hasNext()) {
Pet p = it.next();
System.out.print(p.id() + ":" + p + " ");
}
System.out.println();
// A simpler approach, when possible:
for(Pet p : pets)
System.out.print(p.id() + ":" + p + " ");
System.out.println();
// An Iterator can also remove elements:
it = pets.iterator();
for(int i = 0; i < 6; i++) {
it.next();
it.remove();
}
System.out.println(pets);
}
}
考虑创建一个display方法,它不必知晓容器的确切类型:
public class CrossContainerIteration {
public static void display(Iterator it) {
while(it.hasNext()) {
Pet p = it.next();
System.out.print(p.id() + ":" + p + " ");
}
System.out.println();
}
public static void main(String[] args) {
ArrayList pets = Pets.arrayList(8);
LinkedList petsLL = new LinkedList(pets);
HashSet petsHS = new HashSet(pets);
// TreeSet petsTS = new TreeSet(pets);
display(pets.iterator());
display(petsLL.iterator());
display(petsHS.iterator());
// display(petsTS.iterator());
}
}
display方法不包含任何有关它所遍历的序列的类型信息,而这也显示了Iterator的真正威力,能够将遍历序列的操作与序列底层的结构分离。
ListIterator
ListIterator是一个更加强大的Iterator的子类型。她只能用于各种List类的访问,尽管Iterator只能向前移动,但是ListIterator可以双向移动,它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引。
public class ListIteration {
public static void main(String[] args) {
List pets = Pets.arrayList(8);
ListIterator it = pets.listIterator();
while(it.hasNext())
System.out.print(it.next() + ", " + it.nextIndex() +
", " + it.previousIndex() + "; ");
System.out.println();
// Backwards:
while(it.hasPrevious())
System.out.print(it.previous().id() + " ");
System.out.println();
System.out.println(pets);
it = pets.listIterator(3);
while(it.hasNext()) {
it.next();
it.set(Pets.randomPet());
}
System.out.println(pets);
}
}
LinkedList
LinkedList也像ArrayList一样实现了基本的List接口,但是它执行某些操作(在List中间插入和移除)时比ArrayList更加高效。
LinkedList还添加了可以使其用作栈,队列,双端队列的方法。
例子:
public class LinkedListFeatures {
public static void main(String[] args) {
LinkedList pets =
new LinkedList(Pets.arrayList(5));
print(pets);
// Identical:
print("pets.getFirst(): " + pets.getFirst());
print("pets.element(): " + pets.element());
// Only differs in empty-list behavior:
print("pets.peek(): " + pets.peek());
// Identical; remove and return the first element:
print("pets.remove(): " + pets.remove());
print("pets.removeFirst(): " + pets.removeFirst());
// Only differs in empty-list behavior:
print("pets.poll(): " + pets.poll());
print(pets);
pets.addFirst(new Rat());
print("After addFirst(): " + pets);
pets.offer(Pets.randomPet());
print("After offer(): " + pets);
pets.add(Pets.randomPet());
print("After add(): " + pets);
pets.addLast(new Hamster());
print("After addLast(): " + pets);
print("pets.removeLast(): " + pets.removeLast());
}
}
Stack
LinkedList具有能够直接实现栈的所有功能的方法,因此可以直接将LinkedList当做栈使用,不过了解一下栈的实现可以更好的了解它的内部机制。
public class Stack {
private LinkedList storage = new LinkedList();
public void push(T v) { storage.addFirst(v); }
public T peek() { return storage.getFirst(); }
public T pop() { return storage.removeFirst(); }
public boolean empty() { return storage.isEmpty(); }
public String toString() { return storage.toString(); }
}
进行演示:
public class StackTest {
public static void main(String[] args) {
Stack stack = new Stack();
for(String s : "My dog has fleas".split(" "))
stack.push(s);
while(!stack.empty())
System.out.print(stack.pop() + " ");
}
}
public class StackCollision {
public static void main(String[] args) {
Stack stack =
new Stack();
for(String s : "My dog has fleas".split(" "))
stack.push(s);
while(!stack.empty())
System.out.print(stack.pop() + " ");
System.out.println();
java.util.Stack stack2 =
new java.util.Stack();
for(String s : "My dog has fleas".split(" "))
stack2.push(s);
while(!stack2.empty())
System.out.print(stack2.pop() + " ");
}
}
Set
Set不保存重复元素。
Set具有和Collection完全一样的接口,因此没有额外的功能。实际上,Set就是Collection,只是行为不同。
例子:
public class SetOfInteger {
public static void main(String[] args) {
Random rand = new Random(47);
Set intset = new HashSet();
for(int i = 0; i < 10000; i++)
intset.add(rand.nextInt(30));
System.out.println(intset);
}
}
0到29之间的10000个随机数被添加到了Set中,其中没有重复的数字出现。
并且结果也是乱序的,如果想对结果排序,一种方式是使用TreeSet代替HashSet。
public class SortedSetOfInteger {
public static void main(String[] args) {
Random rand = new Random(47);
SortedSet intset = new TreeSet();
for(int i = 0; i < 10000; i++)
intset.add(rand.nextInt(30));
System.out.println(intset);
}
}
接下来使用contains()测试Set的归属性。
public class SetOperations {
public static void main(String[] argsStr) {
Set set1 = new HashSet();
Collections.addAll(set1,
"A B C D E F G H I J K L".split(" "));
set1.add("M");
print("H: " + set1.contains("H"));
print("N: " + set1.contains("N"));
Set set2 = new HashSet();
Collections.addAll(set2, "H I J K L".split(" "));
print("set2 in set1: " + set1.containsAll(set2));
set1.remove("H");
print("set1: " + set1);
print("set2 in set1: " + set1.containsAll(set2));
set1.removeAll(set2);
print("set2 removed from set1: " + set1);
Collections.addAll(set1, "X Y Z".split(" "));
print("‘X Y Z’ added to set1: " + set1);
}
}
Map
下面,键是由Random随机产生的数字,而值是该数字出现的次数。
public class Statistics {
public static void main(String[] args) {
Random rand = new Random(47);
Map m =
new HashMap();
for(int i = 0; i < 10000; i++) {
// Produce a number between 0 and 20:
int r = rand.nextInt(20);
Integer freq = m.get(r);
m.put(r, freq == null ? 1 : freq + 1);
}
System.out.println(m);
}
}
下面这个使用一个String描述来查找Pet,并且使用containsKey()和containsValue()来测试一个Map,查看它是否包含某个键或者值。
public class PetMap {
public static void main(String[] args) {
Map petMap = new HashMap();
petMap.put("My Cat", new Cat("Molly"));
petMap.put("My Dog", new Dog("Ginger"));
petMap.put("My Hamster", new Hamster("Bosco"));
print(petMap);
Pet dog = petMap.get("My Dog");
print(dog);
print(petMap.containsKey("My Dog"));
print(petMap.containsValue(dog));
}
}
扩展到多维:
public class MapOfList {
public static Map>
petPeople = new HashMap>();
static {
petPeople.put(new Person("Dawn"),
Arrays.asList(new Cymric("Molly"),new Mutt("Spot")));
petPeople.put(new Person("Kate"),
Arrays.asList(new Cat("Shackleton"),
new Cat("Elsie May"), new Dog("Margrett")));
petPeople.put(new Person("Marilyn"),
Arrays.asList(
new Pug("Louie aka Louis Snorkelstein Dupree"),
new Cat("Stanford aka Stinky el Negro"),
new Cat("Pinkola")));
petPeople.put(new Person("Luke"),
Arrays.asList(new Rat("Fuzzy"), new Rat("Fizzy")));
petPeople.put(new Person("Isaac"),
Arrays.asList(new Rat("Freckly")));
}
public static void main(String[] args) {
print("People: " + petPeople.keySet());
print("Pets: " + petPeople.values());
for(Person person : petPeople.keySet()) {
print(person + " has:");
for(Pet pet : petPeople.get(person))
print(" " + pet);
}
}
}
Queue
LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口。
public class QueueDemo {
public static void printQ(Queue queue) {
while(queue.peek() != null)
System.out.print(queue.remove() + " ");
System.out.println();
}
public static void main(String[] args) {
Queue queue = new LinkedList();
Random rand = new Random(47);
for(int i = 0; i < 10; i++)
queue.offer(rand.nextInt(i + 10));
printQ(queue);
Queue qc = new LinkedList();
for(char c : "Brontosaurus".toCharArray())
qc.offer(c);
printQ(qc);
}
}
PriorityQueue
优先级队列声明下一个弹出元素是最需要的元素
当你在PriorityQueue上调用offer方法来插入一个对象时,这个对象会在队列中被排序,默认的排序将使用对象在队列中的自然顺序,但是你可以通过提供自己的Comparator来修改这个顺序。
public class PriorityQueueDemo {
public static void main(String[] args) {
PriorityQueue priorityQueue =
new PriorityQueue();
Random rand = new Random(47);
for(int i = 0; i < 10; i++)
priorityQueue.offer(rand.nextInt(i + 10));
QueueDemo.printQ(priorityQueue);
List ints = Arrays.asList(25, 22, 20,
18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
priorityQueue = new PriorityQueue(ints);
QueueDemo.printQ(priorityQueue);
priorityQueue = new PriorityQueue(
ints.size(), Collections.reverseOrder());
priorityQueue.addAll(ints);
QueueDemo.printQ(priorityQueue);
String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
List strings = Arrays.asList(fact.split(""));
PriorityQueue stringPQ =
new PriorityQueue(strings);
QueueDemo.printQ(stringPQ);
stringPQ = new PriorityQueue(
strings.size(), Collections.reverseOrder());
stringPQ.addAll(strings);
QueueDemo.printQ(stringPQ);
Set charSet = new HashSet();
for(char c : fact.toCharArray())
charSet.add(c); // Autoboxing
PriorityQueue characterPQ =
new PriorityQueue(charSet);
QueueDemo.printQ(characterPQ);
}
}
Collection和Iterator
Collection是描述所有序列容器的共性的接口,它可能被认为是一个“附属接口”,因为要表示其他若干个接口的共性而出现的接口。另外,java.utiL.AbstractCollection提供了Collection的默认实现,使得你可以创建AbstractCollection的子类型,而其中没有不必要的代码重复。
用迭代器而不是Collection来表示容器之间的共性,但是,这两种方法绑定到了一起,因为实现Collection就意味着需要提供iterator()方法。
public class InterfaceVsIterator {
public static void display(Iterator it) {
while (it.hasNext()) {
Pet p = it.next();
System.out.print(p.id() + ":" + p + " ");
}
System.out.println();
}
public static void display(Collection pets) {
for (Pet p : pets)
System.out.print(p.id() + ":" + p + " ");
System.out.println();
}
public static void main(String[] args) {
List petList = Pets.arrayList(8);
Set petSet = new HashSet(petList);
Map petMap =
new LinkedHashMap();
String[] names = ("Ralph, Eric, Robin, Lacey, " +
"Britney, Sam, Spot, Fluffy").split(", ");
for (int i = 0; i < names.length; i++)
petMap.put(names[i], petList.get(i));
display(petList);
display(petSet);
display(petList.iterator());
display(petSet.iterator());
System.out.println(petMap);
System.out.println(petMap.keySet());
display(petMap.values());
display(petMap.values().iterator());
}
}
两个版本的display方法都可以使用Map或Collection的子类型来工作,而且Collection接口和Iterator都可以将display方法以底层容器的特定实现解耦。
public class CollectionSequence extends AbstractCollection {
private Pet[] pets = Pets.createArray(8);
public int size() { return pets.length; }
public Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
return index < pets.length;
}
public Pet next() { return pets[index++]; }
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args) {
CollectionSequence c = new CollectionSequence();
InterfaceVsIterator.display(c);
InterfaceVsIterator.display(c.iterator());
}
}
上面可以看出,如果你实现Collection,就必须实现iterator(),只拿实现iterator()与继承AbstractCollection相比,花费的代价只是略小。
如果你的类已经继承其它类,那么你要实现Collection就必须实现该接口中的所有方法。
此时,继承并提供创建迭代器的能力就会容易的多。
class PetSequence {
protected Pet[] pets = Pets.createArray(8);
}
public class NonCollectionSequence extends PetSequence{
public Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
return index < pets.length;
}
public Pet next() { return pets[index++]; }
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args) {
NonCollectionSequence nc = new NonCollectionSequence();
InterfaceVsIterator.display(nc.iterator());
}
}
Foreach与迭代器
foreach语法主要用于数组,但是也可以用于任何Collection对象
public class ForEachCollections {
public static void main(String[] args) {
Collection cs = new LinkedList();
Collections.addAll(cs,
"Take the long way home".split(" "));
for(String s : cs)
System.out.print("‘" + s + "‘ ");
}
}
上边之所以能够工作是因为Java SE5引入了新的Iterable接口,该接口包含一个能够产生
Iterator的iterator方法,并且Iterable接口被foreach用来在序列中移动,因此如果你创建了任何实现Iterable的类,都可以将它用在foreach语句中。
public class IterableClass implements Iterable{
protected String[] words = ("And that is how " +
"we know the Earth to be banana-shaped.").split(" ");
public Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
return index < words.length;
}
public String next() { return words[index++]; }
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args) {
for(String s : new IterableClass())
System.out.print(s + " ");
}
}
iterator方法返回的是实现了Iterator
在Java SE5中,大量的类都是Iterable类型,主要包括所有的Collection类(不包括Map类)。
下面可以显示所有操作系统的环境变量:
public class EnvironmentVariables {
public static void main(String[] args) {
for(Map.Entry entry: System.getenv().entrySet()) {
System.out.println(entry.getKey() + ": " +
entry.getValue());
}
}
}
foreach语句可以用于数组或者其他任何Iterable,但是这并不意味着数组肯定也是一个Iterable,而且任何自动包装也不会自动发生
public class ArrayIsNotIterable {
static void test(Iterable ib) {
for(T t : ib)
System.out.print(t + " ");
}
public static void main(String[] args) {
test(Arrays.asList(1, 2, 3));
String[] strings = { "A", "B", "C" };
// An array works in foreach, but it’s not Iterable:
//! test(strings);
// You must explicitly convert it to an Iterable:
test(Arrays.asList(strings));
}
}