【JDK1.8源码剖析】内部迭代器 Iterable接口
文章目录
Iterable源码分析
(一)简介
Iterable是从jdk1.5就存在的接口,称为内部迭代器,常用作容器类的接口,以支持遍历操作(同时支持流式遍历)
内部迭代器的特点是嵌入,其迭代行为必须在容器对象内部实现(借助了外部比较器)。一个类如果实现了Iterable接口,就意味着“该类本身支持遍历”,并可以通过for-each这种循环语法来直接遍历。当然,一个类如果没有实现Iterable接口,也可以通过挂载外部迭代器Iterator进行遍历。
此外,内部迭代器还可转换为可分割迭代器Spliterator,以便用于流式操作
注意区别于外部迭代器Iterator和枚举器Enumeration
它就只有三个API。
// since 1.5
Iterator<T> iterator();
// since 1.8
default void forEach(Consumer<? super T> action){}
// since 1.8
default Spliterator<T> spliterator(){}
(二)源码分析
???? 首先看第一个API,它的功能很简单就是返回T元素类型的迭代器
// 返回T元素类型的迭代器
Iterator<T> iterator();
???? 再看第二个APi
// 对Iterable的每个元素执行给定操作(函数式接口),直到处理完所有元素或操作引发异常。
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
Consumer 是用得非常多的函数式接口
@FunctionalInterface
public interface Consumer<T>
所谓的函数式接口,是在接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method
interfaces。
它们主要用在Lambda表达式和方法引用(实际上也可认为是Lambda表达式)上。
比如说定义了一个函数式接口如下:
@FunctionalInterface
interface MakeFood{
void doSomething();
}
那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的)
MakeFood makeFood = ()-> System.out.println("蒸包子");
你会注意到定义函数式接口的时候有一个注解 @FunctionalInterface
这是Java 8为函数式接口引入的新注解,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
比如说接口中包含了两个抽象方法,这就违反了函数式接口的定义,一般IDE会提示你。
在Java 8 中内置内置四大核心函数式接口,第一个就是Consumer
演示一下:
import org.junit.Test;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
/*
* Java8 内置的四大核心函数式接口
*
* Consumer<T> : 消费型接口
* void accept(T t);
*
* Supplier<T> : 供给型接口
* T get();
*
* Function<T, R> : 函数型接口
* R apply(T t);
*
* Predicate<T> : 断言型接口
* boolean test(T t);
*
*/
public class MyTest {
//Predicate<T> 断言型接口:
@Test
public void test4(){
List<String> list = Arrays.asList("Hello", "lala", "Lambda", "www", "ok");
List<String> strList = filterStr(list, (s) -> s.length() > 3);
for (String str : strList) {
System.out.println(str);
}
}
//需求:将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for (String str : list) {
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
//Function<T, R> 函数型接口:
@Test
public void test3(){
String newStr = strHandler("\t\t\t 我和我的祖国 ", (str) -> str.trim());
System.out.println(newStr);
String subStr = strHandler("一刻也不能分离", (str) -> str.substring(2, 5));
System.out.println(subStr);
}
//需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun){
return fun.apply(str);
}
//Supplier<T> 供给型接口 :
@Test
public void test2(){
List<Integer> numList = getNumList(10, () -> (int)(Math.random() * 100));
for (Integer num : numList) {
System.out.println(num);
}
}
//需求:产生指定个数的整数,并放入集合中
private List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer n = sup.get();
list.add(n);
}
return list;
}
//Consumer<T> 消费型接口 :
@Test
public void test1(){
happy(10000, (m) -> System.out.println("吃喝玩乐花了" + m + "元"));
}
private void happy(double money, Consumer<Double> con){
con.accept(money);
}
}
相比说到这里你就明白了,我们可以通过函数式接口实现 遍历每个元素时,并对其执行相应的择取操作。
至于Lambda表达式的详细内容可以参考我这篇文章【Java 8 in Action】Lambda表达式
我们看一个该方法的一个落地点 – ArrayList中对forEach进行了实现
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
以循环打印Arraylist中的值为例,在java8之前的写法是
for(Integer i : list) {
System.out.println(i);
}
在java8中可以写成
list.forEach(x -> System.out.print(x));
比之前舒服多了,可是虽然forEach使代码看上去更简洁,但是从效率上看却是相反的,最原始的循环效率最高,操作越方便的反而性能会下降,操作越方便的方法其内部都是层层调用
不过list.stream().forEach支持多线程并行操作,这样子的话,在项目中效率反而可能会提升。
???? 最后一个API
// 关注 Spliterator接口
// 将普通迭代器转换为可分割迭代器,用于流式操作
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
Spliterator(splitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历。
上一篇: Android事件分发剖析
下一篇: php微信公众号开发之微信企业付款给个人