迭代器如何使用
原本的遍历是这样的,
List<Integer> list = new ArrayList<>();
//add
for(int i = 0 ;i < list;i++ ){
System.out.println(list.get(i));
}
而用了迭代器之后可以是这样的
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
或者这样,
for (Integer i:list){
System.out.println(i);
}
java8 之后可以用
list.forEach(item -> System.out.println(item));
//或者更加精简
list.forEach(System.out::println);
为何要用迭代器?
举个例子,比如要遍历 LinkedList
如果不用迭代器,有些新手会写成这样
for(int i = 0 ; i < linkedList.size(); i++){
System.out.println(list.get(i));
}
知道,LinkedList 的数据结构的朋友,应该会知道这样遍历的时候复杂度会是 O(n^2) 而 LinkedList 的遍历本应该是 O(n)。 这只是个简单的例子,以小见大吧。面对未知的数据结构,或者在不熟悉项目且时候很紧,逼着要用以前的人写的数据结构的时候,如果还要用最原始的方式进行遍历,就会莫名其妙地给程序“留有优化的空间”。
也就是说,如果用了遍历器模式,就在原来的数据结构上建立一个新的抽象,更加方便地进行遍历。
构造一个迭代器
从上面一个例子可以看到
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
比如有一个类是 Teacher ,他管理了很多学生。想通过这种方式遍历要怎样做呢?这样就可以了。
public class Teacher {
Student students[];
public Teacher(Student[] students) {
this.students = students;
}
public Iterator<Student> iterator() {
return new Iterator<Student>() {
int i = 0;
@Override
public boolean hasNext() {
return students != null && i != students.length;
}
@Override
public Student next() {
return students[i++];
}
};
}
}
如果是要做到类似容器那种 for-each 的效果?比如是这样。
for (Student s : teacher) {
System.out.println(s);
}
teacher.forEach(System.out::println);
那就要额外实现一下 Iterable 接口了。
public class Teacher implements Iterable<Student> {
//...
@Override
public Iterator<Student> iterator() {
//...同上
}
@Override
public void forEach(Consumer<? super Student> action) {
for(Student s: students){
action.accept(s);
}
}
/**
* 这个是用于 java8 的并行 foreach 的,运用的模式类似于迭代是,比较复杂先不写了。
**/
@Override
public Spliterator<Student> spliterator() {
//TODO
return null;
}
}
最后看看各个不同写法的迭代的性能
用 100 万条 简单测试一下
int size = 1000000;
List<Integer> nums = new ArrayList<>();
for (int i = 0; i < size; i++) {
nums.add(i);
}
//传统方式 用时 6ms
long start = System.currentTimeMillis();
for(int i = 0 ; i <size ;i++){
nums.get(i);
}
long end = System.currentTimeMillis();
System.out.println("传统方式:" + (end - start));
//使用迭代器 用时:16ms
start = System.currentTimeMillis();
Iterator<Integer> iterator = nums.iterator();
while (iterator.hasNext()) {
iterator.next();
}
end =System.currentTimeMillis();
System.out.println("迭代器:" + (end - start));
//使用 foreach 语法糖 用时:17ms
start = System.currentTimeMillis();
for (Integer num : nums) {
}
end = System.currentTimeMillis();
System.out.println("foreach:" + (end - start));
//使用 lambda 接口 语法糖 用时:39ms
start = System.currentTimeMillis();
nums.forEach(num -> {});
end = System.currentTimeMillis();
System.out.println("lambda:" + (end - start));
//使用 stream 接口语法糖 用时: 14ms
start = System.currentTimeMillis();
nums.stream().forEach(s->{});
end = System.currentTimeMillis();
System.out.println("stream 接口语法糖:" + (end - start));