欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【JDK1.8源码剖析】内部迭代器 Iterable接口

程序员文章站 2022-05-19 16:14:27
...

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
【JDK1.8源码剖析】内部迭代器 Iterable接口
演示一下:

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);
    }
}

【JDK1.8源码剖析】内部迭代器 Iterable接口

相比说到这里你就明白了,我们可以通过函数式接口实现 遍历每个元素时,并对其执行相应的择取操作。

至于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,但一个是顺序遍历,一个是并行遍历。

相关标签: JDK源码剖析