第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Lambda 表達式教程 Lambda 表達式教程 函數(shù)式數(shù)據(jù)處理

函數(shù)式數(shù)據(jù)處理

Java 8 中新增的特性其目的是為了幫助開發(fā)人員寫出更好地代碼,其中關(guān)鍵的一部分就是對核心類庫的改進。流( Stream )和集合類庫便是核心類庫改進的內(nèi)容。

1. 從外部迭代到內(nèi)部迭代

對于一個集合迭代是我們常用的一種操作,通過迭代我們可以處理返回每一個操作。常用的就是 for 循環(huán)了。我們來看個例子:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.Arrays;
import java.util.List;

public class Test{
	public static void main(String...s){  
		 List<Integer> numbers = Arrays.asList(new Integer[]{1,2,3,4,5,6,7});
		 int counter = 0;  
		 for(Integer integer : numbers){  
			 if(integer > 5) counter++;   
		 }  
		 System.out.println(counter);  
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出: 2

這里我們統(tǒng)計數(shù)組 numbers 中大于 5 的元素的個數(shù),我們通過 for 循環(huán)對 numbers 數(shù)組進行迭代,隨后對每一個元素進行比較。這個調(diào)用過程如下:

圖片描述

在這個過程中,編譯器首先會調(diào)用 Listiterator() 方法產(chǎn)生一個 Iterator 對象來控制迭代過程,這個過程我們稱之為 外部迭代。 在這個過程中會顯示調(diào)用 Iterator 對象的 hasNext()next() 方法來完成迭代。

這樣的外部迭代有什么問題呢?

Tips: 對于循環(huán)中不同操作難以抽象。

比如我們前面的例子,假設(shè)我們要對大于 5 小于 5 和等于 5 的元素分別進行統(tǒng)計,那么我們所有的邏輯都要寫在里面,并且只有通過閱讀里面的邏輯代碼才能理解其意圖,這樣的代碼可閱讀性是不是和 Lambda 表達式的可閱讀性有著天壤之別呢?

Java 8 中提供了另一種通過 Stream 流來實現(xiàn) 內(nèi)部迭代 的方法。我們先來看具體的例子:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.Arrays;

public class Test{
	public static void main(String...s){
	    List<Integer> numbers = Lists.newArrayList(1,2,3,4,5,6,7);
	    long counter = numbers.stream().filter(e->e>5).count();
	    System.out.println(counter);
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出: 2

在這個例子中,我們調(diào)用 stream() 方法獲取到 Stream 對象,然后調(diào)用該對象的 filter() 方法對元素進行過濾,最后通過 count() 方法來計算過濾后的 Stream 對象中包含多少個元素對象。

圖片描述

與外部迭代不同,內(nèi)部迭代并不是返回控制對象 Iterator, 而是返回內(nèi)部迭代中的相應(yīng)接口 Stream。進而把對集合的復(fù)雜邏輯操作變成了明確的構(gòu)建操作。

在這個例子中,通過內(nèi)部迭代,我們把整個過程被拆分成兩步:

  1. 找到大于 5 的元素;
  2. 統(tǒng)計這些元素的個數(shù)。

這樣一來我們代碼的可讀性是不是大大提升了呢?

2. 實現(xiàn)機制

在前面的內(nèi)部迭代例子中,整個操作過程被分成了過濾和計數(shù)兩個簡單的操作。我們再來看一下之前的例子,并做了一些改造:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

public class Test{
    public static void main(String...s){
        List<Integer> numbers = new ArrayList<Integer>();
        Collections.addAll(numbers,new Integer[]{1,2,3,4,5,6,7});
        Stream stream1 = numbers.stream();
        numbers.remove(6);
        //直接使用numbers的stream()
        long counter = numbers.stream().filter(e->e>5).count();
        System.out.println(counter);
        //調(diào)用之前的stream1
        counter = stream1.filter(ex-> (Integer)ex>5).count();
        System.out.println(counter);
    }
}
運行案例 點擊 "運行案例" 可查看在線運行效果

返回結(jié)果:

1  
1

在這個例子中,我們在獲取到 Stream 對象 stream1 后刪除了數(shù)組 numbers 中的最后一個元素,隨后分別對 numbers 和 stream1 進行過濾統(tǒng)計操作,會發(fā)現(xiàn)兩個結(jié)果是一樣的,stream1 中的內(nèi)容跟隨 numbers 一起做相應(yīng)的改變。這說明 Stream 對象不是一個新的集合,而是創(chuàng)建新集合的配方。同樣,像 filter() 雖然返回 Stream 對象,但也只是對 Stream 的刻畫,并沒有產(chǎn)生新的集合。

我們通常對于這種不產(chǎn)生集合的方法叫做 惰性求值方法,相對應(yīng)的類似于 count() 這種返回結(jié)果的方法我們叫做 及早求值方法

我們可以把多個惰性求值方法組合起來形成一個惰性求值鏈,最后通過及早求值操作返回想要的結(jié)果。這類似建造者模式,使用一系列操作設(shè)置屬性和配置,最后通過一個 build 的方法來創(chuàng)建對象。通過這樣的一個過程我們可以讓我們對集合的構(gòu)建過程一目了然。這也就是 Java 8 風(fēng)格的集合操作。

3. 常用的流操作

為了更好地理解 Stream API,我們需要掌握它的一些常用操作,接下來我們將逐個學(xué)習(xí)幾種常用的操作。

3.1 collect

collect 操作是根據(jù) Stream 里面的值生成一個列表,它是一個求值操作。

Tips: collect 方法通常會結(jié)合 Collectors 接口一起使用,是一個通用的強大結(jié)構(gòu),可以滿足數(shù)據(jù)轉(zhuǎn)換、數(shù)據(jù)分塊、字符串處理等操作。

我們來看一些例子:

  1. 生成集合:
實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.stream.Stream;
import java.util.List;
import java.util.stream.Collectors;

public class Test{
	public static void main(String...s){  
		List<String> collected = Stream.of("a","b","c").collect(Collectors.toList());  
		System.out.println(collected);  
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出:[a, b, c]

使用 collect(Collectors.toList()) 方法從 Stream 中生成一個列表。

  1. 集合轉(zhuǎn)換:

使用 collect 來定制集合收集元素。

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.TreeSet;

public class Test{
	public static void main(String...s){
		List<String> collected = Stream.of("a","b","c","c").collect(Collectors.toList());
		TreeSet<String> treeSet = collected.stream().collect(Collectors.toCollection(TreeSet::new));
		System.out.println(collected);
		System.out.println(treeSet);
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出結(jié)果: 
[a, b, c, c]
[a, b, c]

使用 toCollection 來定制集合收集元素,這樣就把 List 集合轉(zhuǎn)換成了 TreeSet

  1. 轉(zhuǎn)換成值:

使用 collect 來對元素求值。

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;

public class Test{
	public static void main(String...s){
		List<String> collected = Stream.of("a","b","c").collect(Collectors.toList());
		String maxChar = collected.stream().collect(Collectors.maxBy(String::compareTo)).get();
		System.out.println(maxChar);
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出: c

上面我們使用 maxBy 接口讓收集器生成一個值,通過方法引用調(diào)用了 StringcompareTo 方法來比較元素的大小。同樣還可以使用 minBy 來獲取最小值。

  1. 數(shù)據(jù)分塊:

比如我們對于數(shù)據(jù) 1-7 想把他們分成兩組,一組大于 5 另外一組小于等于 5,我們可以這么做:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import java.util.stream.Collectors;

public class Test{
	public static void main(String...s){
		List<Integer> collected = Stream.of(1,2,3,4,5,6,7).collect(Collectors.toList());
		Map<Boolean,List<Integer>> divided = collected.stream().collect(Collectors.partitioningBy(e -> e>5));
		System.out.println(divided.get(true));
		System.out.println(divided.get(false));
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出結(jié)果:
[6, 7]
[1, 2, 3, 4, 5]

通過 partitioningBy 接口可以把數(shù)據(jù)分成兩類,即滿足條件的和不滿足條件的,最后將其收集成為一個 Map 對象,其 KeyBoolean 類型,Value 為相應(yīng)的集合元素。

同樣我們還可以使用 groupingBy 方法來對數(shù)據(jù)進行分組收集,這類似于 SQL 中的 group by 操作。

  1. 字符串處理:

collect 還可以來將元素組合成一個字符串。

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;

public class Test{
public static void main(String...s){
    List<String> collected = Stream.of("a","b","c").collect(Collectors.toList());
    String formatted = collected.stream().collect(Collectors.joining(",","[","]"));
    System.out.println(formatted);
}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出:[a,b,c]

這里我們把 collected 數(shù)組的每個元素拼接起來,并用 [ ] 包裹。

3.2 map

map 操作是將流中的對象換成一個新的流對象,是 Stream 上常用操作之一。 其示意圖如下:

圖片描述
?

比如我們把小寫字母改成大寫,通常我們會使用 for 循環(huán):

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

public class Test{
public static void main(String...s){
    List<String>  collected = new ArrayList<>();
     List<String>  newArr = new ArrayList<>();
    Collections.addAll(newArr,new String[]{"a","b","c"});
    for(String string : newArr){
        collected.add(string.toUpperCase());
    }
    System.out.println(collected);
}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出: [A, B, C]

此時,我們可以使用 map 操作來進行轉(zhuǎn)換:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;

public class Test{
	public static void main(String...s){
	    List<String> collected = Stream.of("a","b","c").collect(Collectors.toList());
	    List<String> upperCaseList = collected.stream().map(e->e.toUpperCase()).collect(Collectors.toList());
	    System.out.println(upperCaseList);    
	}
}

運行案例 點擊 "運行案例" 可查看在線運行效果
輸出: [A, B, C]

map 操作中,我們 把 collected 中的每一個元素轉(zhuǎn)換成大寫,并返回。

3.3 flatmap

flatmapmap 功能類似,只不過 map 對應(yīng)的是一個流,而 flatmap 可以對應(yīng)多個流。

我們來看一個例子:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;

public class Test{
	public static void main(String...s){
	    List<String> nameA = Arrays.asList("Mahela", "Sanga", "Dilshan");
	    List<String> nameB = Arrays.asList("Misbah", "Afridi", "Shehzad");
	    List<List<String>> nameSets = Arrays.asList(nameA,nameB);
	    List<String> flatMapList = nameSets.stream()
	            .flatMap(pList -> pList.stream())
	            .collect(Collectors.toList());
	    System.out.println(flatMapList);
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
返回結(jié)果: [Mahela, Sanga, Dilshan, Misbah, Afridi, Shehzad]

通過 flatmap 我們把集合 nameSets 中的字集合合并成了一個集合。

3.4 filter

filter 用來過濾元素,在元素遍歷時,可以使用 filter 來提取我們想要的內(nèi)容,這也是集合常用的方法之一。其示意圖如下:

圖片描述
?

我們來看一個例子:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.stream.Collectors;


public class Test{
	public static void main(String...s) {
	    List<Integer> numbers = new ArrayList<>();
	    Collections.addAll(numbers,new Integer[]{1,2,3,4,5,6,7});
	    List<Integer> collected = numbers.stream()
									    .filter(e->e>5).collect(Collectors.toList());
	    System.out.println(collected);
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出:[6, 7]

此時,filter 會遍歷整個集合,將滿足將滿足條件的元素提取出來,并通過收集器收集成新的集合。

3.5 max/min

max/min 求最大值和最小值也是集合上常用的操作。它通常會與 Comparator 接口一起使用來比較元素的大小。示例如下:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.List;
import java.util.Collections;

public class Test{
	public static void main(String...s) {
	    List<Integer> numbers = new ArrayList<>();
	    Collections.addAll(numbers,new Integer[]{1,2,3,4,5,6,7});
	    Integer max = numbers.stream().max(Comparator.comparing(k->k)).get();
	    Integer min = numbers.stream().min(Comparator.comparing(k->k)).get();
	    System.out.println("max:"+max);
	    System.out.println("min:"+min);
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出:
max:7
min:1

我們可以在 Comparator 接口中定制比較條件,來獲得想要的結(jié)果。

3.6 reduce

reduce 操作是可以實現(xiàn)從流中生成一個值,我們前面提到的如 countmax、min 這種及早求值就是由reduce 提供的。我們來看一個例子:

實例演示
預(yù)覽 復(fù)制
復(fù)制成功!
import java.util.stream.Stream;

public class Test{
	public static void main(String...s) {
	    int sum = Stream.of(1,2,3,4,5,6,7).reduce(0,(acc,e)-> acc + e);
	    System.out.println(sum);
	}
}
運行案例 點擊 "運行案例" 可查看在線運行效果
輸出:28

上面的例子是對數(shù)組元素進行求和,這個時候我們就要使用 reduce 方法。這個方法,接收兩個參數(shù),第一個參數(shù)相當(dāng)于是一個初始值,第二參數(shù)則為具體的業(yè)務(wù)邏輯。 上面的例子中,我們給 acc 參數(shù)賦予一個初始值 0 ,隨后將 acc 參數(shù)與各元素求和。

4. 小結(jié)

以上我們學(xué)習(xí)了 Java 8 的流及常用的一些集合操作。我們需要常用的函數(shù)式接口和流操作非常熟悉才能更好地使用這些新特性。

另外,請思考一個問題,在本節(jié)關(guān)于集合的操作中都將集合通過 stream() 方法轉(zhuǎn)換成了 Stream 對象,那么我們還有必要對外暴露一個集合對象(List 或者 Set)嗎?

Tips: 在編程過程中,使用 Stream 工廠比對外暴露集合對象要更好一些。僅需要暴露 Stream 接口,在實際操作中無論怎么使用都影響內(nèi)部的集合。

所以,Java 8 風(fēng)格不是一蹴而就的,我們可以對已有的代碼進行重構(gòu)來練習(xí)和強化 Java 8 的編程風(fēng)格,時間長了自然就對 Stream 對象有更深的理解了。