Iterable源码分析

Iterable 源码分析

Iterable简介

  Iterable是从jdk1.5就存在的接口,其实我们经常用到它的功能,就是for-each,要想使用for-each,就必须实现此接口。

API简介

// since 1.5
Iterator<T> iterator();
// since 1.8
default void forEach(Consumer<? super T> action){}
// since 1.8
default Spliterator<T> spliterator(){}

源码分析

public interface Iterable<T> {
   	// 返回T元素类型的迭代器
    Iterator<T> iterator();

  	// 对Iterable的每个元素执行给定操作,直到处理完所有元素或操作引发异常。
    // 除非实现类另有指定,否则操作按迭代顺序执行(如果指定了迭代顺序)
    // Consumer 四大函数接口,不在本章叙述范围内 后续会有文章专门讲解java8函数编程
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    
    // 可分割的迭代器,是1.8退出的用于并行遍历元素而设计的一个迭代器
    	 // 官方文档说明默认的实现分割的能力比较差,推荐覆盖默认实现。
    	 // 可以跟上面的Iterator功能区分;一个是顺序遍历,一个是并行遍历
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

foreach遍历、for循环和Iterable的渊源

  • for 循环遍历集合 以及反编译后的代码
ArrayList<Integer> a1 = new ArrayList<Integer>(16);
/*0-5*/
for (int i = 0; i < 6; i++) {
a1.add(i);
}
for (int i = 0; i < 6; i++) {
System.out.println(i);
}
/*************反编译后的代码**********************/
ArrayList<Integer> a1 = new ArrayList(16);
int i;
for(i = 0; i < 6; ++i) {
a1.add(i);
}
for(i = 0; i < 6; ++i) {
System.out.println(i);
}
  • foreach遍历集合以及反编译后的代码
ArrayList<Integer> a1 = new ArrayList<Integer>(16);
/*0-5*/
for (int i = 0; i < 6; i++) {
a1.add(i);
}
// foreach循环
for(Integer i : a1){
System.out.println(i);
}
/***************反编译后的代码*******************/
ArrayList<Integer> a1 = new ArrayList(16);

for(int i = 0; i < 6; ++i) {
a1.add(i);
}
// 反编译之后发现是调用了集合的Iterator来实现的
Iterator var4 = a1.iterator();

while(var4.hasNext()) {
Integer i = (Integer)var4.next();
System.out.println(i);
}
  • foreach遍历数组以及反编译后的代码
String[] str = {"a", "b", "c", "d"};
for (String s :
str) {
System.out.println(s);
}
/***************反编译后的代码**********************/
String[] str = new String[]{"a", "b", "c", "d"};
String[] var2 = str;
int var3 = str.length;

for(int var4 = 0; var4 < var3; ++var4) {
String s = var2[var4];
System.out.println(s);
}

从上面几个例子可以知道:
1:foreach循环底层调用的是iterator这个说法是错的,应该针对性的讲。对于数组,>foreach底层的实现是简单的for循环,而对于集合,底层的实现则是通过Iterator来实现的
2:只有实现了Itearable接口的类才能使用foreach遍历也是错的。对于数组,foreach的底层实现则是for,它并没有实现iterator接口


迭代器原理

java 集合类库的迭代器跟其他类库的迭代器在概念上有着重要的区别。比如:C++的标准模板库的迭代器是根据数组索引建模的。如果给定这样一个迭代器,就可以查看指定位置上的元素,就像是知道数组索引i,就可以查看数组元素a[i]一样,不需要查找元素,就可以将迭代器向前移动一个位置。但是Java迭代器并不是如此。java迭代器查找操作和位置变更是紧密相连的,查找元素的唯一方式就是调用next,而在执行查找的同时,迭代器位置随之向前移动,因此,应该将java迭代器 认为是位于两个元素之间。当调用next时候,迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用

Iterable源码分析

很明显从图中可知,当调用it.next的时候,会查找是否有元素,然后迭代器的指向从A到B位置。

实战演练

  • Iterator 遍历元素
String[] s = {"a", "b", "c", "d", "e"};
List<String> list = Arrays.asList(s);
Iterator<String> itr = list.iterator();
while (itr.hasNext()){
System.out.println(itr.next());
}
  • 循环输出字符串数组中的内容
String[] s = {"a", "b", "c", "d", "e"};
List<String> list1 = Arrays.asList(s);
// lambda表达式
list1.forEach(cc -> System.out.println(cc));
  • 并行遍历元素
String[] str = {"a", "b", "c", "d", "e"};
List<String> list = Arrays.asList(str);
Spliterator<String> s = list.spliterator();
Spliterator<String> s1 =  s.trySplit();
// 返回值是a,b  split 分割成两个集合
// 一个为 a,b  一个为 c,d,e
s.forEachRemaining(System.out :: println);
s1.forEachRemaining(System.out :: println);

总结

  1. 并不是实现了Iterable接口的类才能使用foreach遍历,数组就没有实现Iterable接口,数组使用foreach,反编译后的代码其实是通过for循环来完成这个遍历的功能。
  2. 1.8新增了两个默认实现:一个是foreach,一个是Spliterator
  3. foreachSpliterator一个是顺序遍历元素,一个是并行遍历元素