【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,但一個是順序遍歷,一個是並行遍歷。

發佈了194 篇原創文章 · 獲贊 3472 · 訪問量 53萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章