通常情况下,我们使用集合框架时会希望能够遍历一个集合中的元素。例如,显示集合中的每个元素。 一般的,我们都是采用for循环或增强for(for-each),这两个方法也可以用在集合框架。但是在集合框架中还有一种方法是采用迭代器遍历集合框架,它是一个对象,实现了Iterator接口或ListIterator接口。
迭代器(Iterator),使你能够通过循环来得到或删除集合的元素。ListIterator,继承了Iterator以允许双向遍历列表和修改元素。
两个问题 什么是foreach循环? for-each是增强型的for循环,是Java5的新特性之一。 foreach语法格式如下:
1 2 3 for (元素类型t 元素变量x : 遍历对象obj){ 引用了x的java语句 }
什么时候可以使用foreach进行遍历? 可以被foreach遍历的对象分两种:
数组 java对于数组的foreach处理相对来说简单。在编译期将foreach还原成简单的for循环。实现了Iterable接口的对象 实现了Iterable
接口的对象,可以被foreach。并且java会通过迭代器的形式来遍历他。Iterable接口: 故名思议,实现了这个接口的集合对象支持迭代,是可迭代的。Iterator接口: 迭代器,它就是提供迭代机制的对象,具体如何迭代,都是Iterator
接口规范的。Iterable和Iterator Iterable 我们看看Iterable
接口的用途是什么:
1 2 3 4 5 6 7 8 9 10 11 12 /** * Implementing this interface allows an object to be the target of * the "for-each loop" statement. See * <strong> * <a href="{@docRoot}/../technotes/guides/language/foreach.html">For-each Loop</a> * </strong> * * @param <T> the type of elements returned by the iterator * * @since 1.5 * @jls 14.14.2 The enhanced for statement */
实现这个接口,允许一个对象成为for-each的目标
再来看看接口中定义了哪些需要实现的方法
Iterable.java 1 2 3 4 5 6 7 8 public interface Iterable <T > { Iterator<T> iterator () ; }
上面我们说过了,实现了Iterable
接口的对象,可以被foreach。并且java会通过迭代器 的形式来遍历他。Iterable
接口中的Iterator<T> iterator()
方法返回的就是一个迭代器(Iterator)
Iterator 包含2个需要实现的方法:hasNext()
、next()
和一个默认方法remove()
。 我们以ArrayList
为例:
1 2 List<Object> list=new ArrayList<>(); for (Object obj:list);
编译后查看class文件:
1 2 3 4 5 List<Object> list = new ArrayList(); Object var3; for(Iterator var2 = list.iterator(); var2.hasNext(); var3 = var2.next()) { }
可以看到是使用了迭代器来遍历集合,首先获取集合的迭代器后调用hasNext()
方法判断是否迭代到终点,next()
方法不仅要返回集合元素(下一个),还要将迭代器的游标后移。
定义一个实现了Iterable接口的类 IterableTester.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public class IterableTester <E > implements Iterable <E > { private Object[] elements = {}; private int size = 0 ; IterableTester(int initialCapacity) { if (initialCapacity > 0 ) { elements = new Object[initialCapacity]; } } public boolean add (E element) { if (size < elements.length) { elements[size] = element; size++; return true ; } else { return false ; } } public E get (int index) { return (E) elements[index]; } @Override public Iterator<E> iterator () { return new itr(); } private class itr implements Iterator <E > { int cursor = 0 ; @Override public boolean hasNext () { return cursor != size; } @Override public E next () { if (cursor >= size || cursor >= elements.length) throw new RuntimeException(); return (E) elements[cursor++]; } } }
执行for-each的时候,会先调用iterator()
方法返回一个迭代器。然后调用迭代器的hasNext()
和next()
方法来进行遍历。
测试样例1 1 2 3 4 5 6 7 IterableTester<String> its = new IterableTester<>(5 ); its.add(new String("i " )); its.add(new String("am " )); its.add(new String("a " )); its.add(new String("coder" )); for (String s : its) System.out.print(s);
输出 i am coder
需要注意的地方 迭代出来的元素都是原集合元素的拷贝 Java集合中保存的元素实质是对象的引用 (可以理解为C中的指针),而非对象本身。 迭代出的元素也就都是引用的拷贝 ,结果还是引用。那么,如果集合中保存的元素是可变类型的,我们就可以通过迭代出的元素修改原集合中的对象。而对于不可变类型,如String
或者基本类型的包装类型Integer
等则不会反应到原集合中。
测试样例2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Main { public static void main (String[] args) { IterableTester<Person> itp = new IterableTester<>(5 ); ArrayList<String> als = new ArrayList<>(); Person p = new Person("Tom" ); itp.add(p); als.add(new String("Tom" )); for (Person p_ : itp) p_.setName("Jack" ); for (String s_ : als) s_ = "Jack" ; System.out.println(itp.get(0 ).getName()); System.out.println(als.get(0 )); } } class Person { private String name; Person(String name) { this .name = name; } public void setName (String name) { this .name = name; } public String getName () { return name; } }
输出 值得思考的问题 既然说:迭代出来的元素都是原集合元素的拷贝,如果集合中保存的元素是可变类型的,我们就可以通过迭代出的元素修改原集合中的对象。那么我们可不可以在将迭代元素修改时却不影响原集合元素呢?答案是肯定的,大概有两个实现的方法(这里只说一下大概的思路):
将集合定义成不可变类,具体可看我的这篇博文: 在实现Iterator#next
方法时,返回集合元素的一个克隆,具体可看我的这篇博文: