1. 程式人生 > 實用技巧 >javaSE8學習之內部迭代與外部迭代本質剖析及流本源分析

javaSE8學習之內部迭代與外部迭代本質剖析及流本源分析

關於Stream在Java8中是佔非常主要的地位的,所以這次對它進行進一步探討【這次基本上都是偏理論的東東,但是理解它很重要~】,其實流跟咱們資料庫學習當中的sql語句的特點是非常非常之像的,為什麼這麼說,下面以這個sql語句舉例說明:

“select name from student where age > 20 and address = ‘beijing’ order by age desc;”

該簡單的sql所要表達的意思是:從student這張表中查詢出年齡>20並且地址=北京的記錄,並且對年齡進行降序排序,排序之後將其名字查找出來。對於sql其實是一個描述性的語言,只描述其行為,而具體如何讓db完成這個行為是沒有暴露出來的,對於該sql所做的工作如果換成咱們的stream來實現那會是個什麼樣子呢,下面寫一下虛擬碼:

首先從源資料中獲得stream:

students.stream();

接著進行條件過濾:age > 20 and address = ‘beijing’,如下:

students.stream().filter(student -> student.getAge() > 20).filter(student -> student.getAddress().equals(“beijing”));

然後對年紀進行排序,這裡寫虛擬碼:

students.stream().filter(student -> student.getAge() > 20).filter(student -> student.getAddress().equals(“beijing”)).sorted(…);

最後將其名字打印出來,如下:
students.stream().filter(student -> student.getAge() > 20).filter(student -> student.getAddress().equals(“beijing”)).sorted(…).forEach(student -> System.out.println(student.getName()));

是不是從表現形式上跟sql語句差不多,Stream也是屬於一種描述性的語句, 整個語句並沒有告訴底層Stream要如何去做,等於只要發一些指令給底層就可以了,具體底層怎麼做完全不用關心。

其實對於這種Stream()的這種方式是一種內部迭待,而如果換成傳統的方式既為外部迭待方式,如下:

複製程式碼
List list = new ArrayList<>();

//首先對資料進行條件過濾
for(int i=0; i < students.size();i++){
Student student = students.get(i);
if(student.getAge() > 20 && student.getAddress().equals(“beijing”)){
list.add(student);
}
}

//接著對其按年齡排序
Collections.sort(list. Comparator()…);

//將名字打印出來
for(Student student : list){
System.out.println(student.getName());
}
複製程式碼
是不是發現跟使用Stream的方式完全沒法相比,相差太多了,而且本質上也有區別,之所以上面傳統的方式為外部迭待,很顯然用到了一箇中間的臨時變數,另外將業務條件跟具體遍歷程式碼都混在一起了,語義上理解也沒這麼容易,假如說對於一個不懂程式碼的人來看這兩種實現方式,肯定是看具有描述性語句的stream的方式更加容易理解。

接下來用圖的方式來闡述一下內部迭待與外部迭待的本質區別:

外部迭待:

針對一個集合:
在這裡插入圖片描述

以咱們這個例子為例,會使用for迴圈對其進行迭待,所以會單獨在集合之外寫入咱們自己的處理邏輯,如下:
在這裡插入圖片描述

其中集合與咱們自己編寫的處理邏輯之間是有清晰的劃分的:

在這裡插入圖片描述
在這裡插入圖片描述

內部迭待:

那對於stream的內部迭待方式表現又如何呢?

首先操作的物件就不是集合啦,而是集合的流,如下:
在這裡插入圖片描述

而接下來內的體現就來了,咱們自己寫的程式碼和流會融合在一起了,而非自己寫的這塊程式碼相對於流是獨立的:
在這裡插入圖片描述

合併到一起有什麼好處呢?這樣流就可以拿到咱們自己所提供的程式碼進行相應的優化,當遇到咱們呼叫的終止操作則將咱們提供的所有中間操作與流之前所提供好的基礎功能進行統一的處理。

其實內部迭待有點像是做填充題一樣,對於一篇寫好的英文文章,空缺了幾個填充選項【將某個單詞或某個語句被扣出來了】,而咱們寫的程式碼其實就是填補這些空缺的地方,而不是完全由自己發揮想象來寫這篇英文文章;而傳統的外部迭代方式則是給一個命題裡面所有的內容都得由自己來編寫。

另外內部迭待與外部迭待還有另外一個最大的區別,那就是內部迭待支援並行處理,而所有的並行處理其stream都已經代替咱們來處理了,也就是從呼叫者的角度來想就不用關心處理並行的問題,而傳統的外部迭待一般都是序列的,如果也想要並行處理那這個並行的東東完全得自己來處理。

對於集合和流最後再來用一句話來概括:
集合關注的是資料與資料儲存本身;流關注的則是對資料的計算。而流與迭待器類似的一點是:流是無法重複使用或消費的。

另外對於流再來說明一下:對於流來說,中間操作都會返回一個Stream物件,而終止操作則不會返回Stream型別,它可能不返回值,也可能返回其它型別的單個值。所以說區分中間操作與終止操作的條件就是看返回的型別,如果是Stream物件一定是中間操作。