foreach语法主要运用于数组,但它也同样可以应用于任何一个Collection对象,看下面一段代码:
package access;
import java.util.*;
public class ForEachCollections {
public static void main(String[] args) {
// TODO Auto-generated method stub
Collection<String> cs = new LinkedList<String>();
Collections.addAll(cs, "It is too hard".split(" "));
for(String s : cs)
System.out.print("'" + s + "'");
}
}
此程序的输出结果为:
cs是一个Collection,能够与foreach一起工作的是所有Collection对象的特性。
之所以它能够工作,是因为引入了Iterable接口,该接口包含一个能够产生Iterator的iterator()方法,并且iterable被foreach用来在序列中移动,如果我们创建了任何实现Iterable的类,都可以应用于foreach当中,看如下一段代码:
package access;
import java.util.*;
public class IterableClass implements Iterable<String>{
protected String[] words =("And that is how "
+ "we know the Earth to be banana-shaped.").split(" ");
public static void main(String[] args) {
// TODO Auto-generated method stub
for(String s : new IterableClass())
System.out.print(s + " ");
}
@Override
public Iterator<String> iterator() {
// TODO Auto-generated method stub
return new Iterator<String>(){
private int index = 0;
public boolean hasNext(){
return index < words.length;
}
public String next(){
return words[index++];
}
};
}
}
此程序的输出结果为:
此例子中包含了一个匿名内部类,该内部类实现了Iterator<String>,用于遍历数组中的所有单词,在main中可以看到IterableClass确实用于foreach中,在foreach循环的过程中,由于IterableClass继承自Itrable接口,所以必须实现Itrable接口的Iterator方法,该方法的实现放在内部类中。通过foreach的循环操作以及Iterator内部实现的方法的被调用实现了数组的遍历。
在后续的JAVA版本中,大量的类都是Iterable类型,主要包括Collection类,但不包括Map,看如下一段代码:
package access;
import java.util.*;
public class EnvironmentVariables {
public static void main(String[] args) {
// TODO Auto-generated method stub
for(Map.Entry entry : System.getenv().entrySet()){
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
该例子用于产生所有系统环境变量。
System.getenv方法用于产生系统变量并以Map的形式返回。
entrySet方法将产生一个由Map.Entry元素构成的Set,所以实际上在foreach中循环的是Set并非Map。
foreach可以运用于数组,但数组并不是Iterable,看如下一段代码:
package access;
import java.util.*;
public class ArrayIsNotIterable {
static <T> void test(Iterable<T> ib){
for(T t : ib)
System.out.print (t + " ");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
test(Arrays.asList(1,2,3));
String[] strings = {"A","B","C"};
//test(strings);
test(Arrays.asList(strings));
}
}
此程序的输出结果为:
当把数组当作Iterable参数传递会导致失败,不存在数组到Iterable的自动转换,我们只能通过手工转化。
如果我们希望可以选择向前或向后的方向迭代一个单词列表,如果直接继承类,那么我们只能替换现有的方法,不能实现选择,我们可以采用适配器方法,适配器部分来自于设计模式,我们必须提供特定的接口满足foreach语句,可以通过添加一个能够产生iterable对象的方法,该对象可以用于foreach。看如下代码:
package access;
import java.util.*;
public class MultiIterableClass extends IterableClass{
public Iterable<String> reversed(){
return new Iterable<String>(){
public Iterator<String> iterator(){
return new Iterator<String>(){
int current = words.length - 1;
public boolean hasNext(){
return current > -1;
}
public String next(){
return words[current--];
}
};
}
};
}
public Iterable<String> randomized(){
return new Iterable<String>(){
@Override
public Iterator<String> iterator() {
// TODO Auto-generated method stub
List<String> shuffled =
new ArrayList<String>(Arrays.asList(words));
Collections.shuffle(shuffled,new Random(40));
return shuffled.iterator();
}
};
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MultiIterableClass mic = new MultiIterableClass();
for(String s : mic.reversed())
System.out.print(s + " ");
System.out.println();
for(String s : mic.randomized())
System.out.print(s + " ");
System.out.println();
for(String s : mic)
System.out.print(s + " ");
}
}
此程序的输出结果为:
上述程序中多次运用了匿名内部类来实现。
第二个方法random并没有创建自己的Iterator,直接返回了被打乱的List的Iterator,Collection.shuffle方法并没有影响到原来的数组,而是打乱了shuffled的引用,因为其创建了一个ArrayList,如果由Arrays.asList方法产生的List直接被打乱,则会彻底打乱数组,如以下一段代码:
package access;
import java.util.*;
public class ModifyingArrayAsList {
public static void main(String[] args) {
// TODO Auto-generated method stub
Random rand = new Random(40);
Integer[] ia = {1,2,3,4,5,6,7,8,9,10};
List<Integer> list1 =
new ArrayList<Integer>(Arrays.asList(ia));
System.out.println("Before shuffling: " + list1);
Collections.shuffle(list1,rand);
System.out.println("After shuffling: " + list1);
System.out.println("array: " + Arrays.toString(ia));
System.out.println();
List<Integer> list2 = Arrays.asList(ia);
System.out.println("Before shuffling: " + list2);
Collections.shuffle(list2,rand);
System.out.println("After shuffling: " + list2);
System.out.println("array: " + Arrays.toString(ia));
}
}
此程序的输出结果为:
如果我们执行的操作会修改这个List,但我们并不想这个List本身被修改的情况下,我们应该创建一个副本来进行操作。