源码分析:内部迭代器 Iterable接口,必须要知道的知识点总结

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

点击关注公众号,利用碎片时间学习

(一)简介

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

源码分析:内部迭代器 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("ttt 我和我的祖国   ", (str) -> str.trim());
        System.out.println(newStr);

        String subStr = strHandler("一刻也不能分离", (str) -> str.substring(25));
        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);
    }
}
源码分析:内部迭代器 Iterable接口,必须要知道的知识点总结

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

我们看一个该方法的一个落地点 – 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,但一个是顺序遍历,一个是并行遍历。

来源:blog.csdn.net/qq_42322103/article/

details/104375703

推荐:

主流Java进阶技术(学习资料分享)

源码分析:内部迭代器 Iterable接口,必须要知道的知识点总结
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。“在看”支持我们吧!

原文始发于微信公众号(Java笔记虾):源码分析:内部迭代器 Iterable接口,必须要知道的知识点总结