JDK8中Lambda深入理解和Stream實踐

1 lambda描述

Lambda 表達式是一種匿名函數,簡單地說,它是沒有聲明的方法,也即沒有訪問修飾符、返回值聲明和名字。

它可以寫出更簡潔、更靈活的代碼。作爲一種更緊湊的代碼風格,使 Java 語言的表達能力得到了提升。

在這裏插入圖片描述
實際上,在開發過程中,也許會寫出如下的代碼

public boolean verifyRequestParam(JSONObject requestParam) {
        JSONArray array = new JSONArray();
        // 創建人臉識別任務,需要頁面傳入dataType, yqId, device, deviceName,deviceLocation,taskType, taskName
        array.add(Constant.DATA_TYPE);

        array.add(TaskEntity.TASK_TYPE);
        array.add(TaskEntity.TASK_NAME);

        array.add(Constant.YQ_ID);

        array.add(Constant.DEVICE);
        array.add(TaskEntity.DEVICE_NAME);
        if (requestParam.containsKey(Constant.THRES)) {
            try {
                float thres = requestParam.getFloatValue(Constant.THRES);
                if (thres < 0 || thres >= 1) {
                    log.error("請傳入範圍[0 - 1)的置信度");
                    return false;
                }

            } catch (JSONException | NumberFormatException e) {
                e.printStackTrace();
                log.error("請傳入有效的浮點數置信度值");
                return false;
            }
        }
        return GeneralUtil.hasAllParams(requestParam, array);
    }

從上面的語句中可以發現,程序首先構造了一個JSONArray,然後一個一個的添加,這很無聊,也會使得代碼很無趣,那麼有沒有一種更具表達力,更凝練的代碼表示形式,即實現相同的以圖,又能使得代碼的表達更加簡潔有力呢?
在Java中lambda表達式就能爲此提供很好的能力。
Lambda主要內容如下:
在這裏插入圖片描述

1.1 表現形式

在這裏插入圖片描述

1.2 方法分類

在這裏插入圖片描述

1.3 語法精簡

在這裏插入圖片描述

1.4 方法引用

在這裏插入圖片描述

1.5 案例實踐

在這裏插入圖片描述

1.6 系統內置函數式接口

在這裏插入圖片描述
具體函數定義參見下圖:
在這裏插入圖片描述
Lambda表達式之函數式接口

1.7 閉包

在這裏插入圖片描述## 1.8 函數式編程採用的觀點和原則
在這裏插入圖片描述

2 Martin管道取代循環

在《重構 改善既有代碼的設計中》,關於循環,Martin Fowler提出了兩個重構手法,給我極大的震撼和指導意義。

2.1 拆分循環

 因爲在開發中,筆者也經常會編寫一些身兼多職的循環,它們會一次做了兩三件事情,不爲別的,只因爲這樣可以只循環一次。但是,Fowoler認爲如果在一次循環中做了兩件事時,當需要修改循環時,你得同時理解兩個循環。如果能夠將循環拆分,讓一個循環一次只做一件事,那麼就能確保每次修改時你只需要理解要修改的那塊代碼的行爲就可以了。
 這次重構手法可能讓許多程序員感到不安,因爲它會迫使你執行兩次循環。對此,Fowler建議:先進行重構,然後再進行性能優化,我得先讓代碼結構變得清晰,才能進一步優化;如果重構之後該循環確實成了性能的瓶頸,屆時再把拆開的循環合到一起也很容易。但實際的情況是,即使處理的列表數據更多一些,循環本身也很少成爲性能瓶頸,更何況拆分出循環來通常還使得一些更加強大的。
這個手法的意義不僅在於拆分出循環本身,而且在於它爲進一步優化提供了良好的起點----下一步我通常會將每個循環提煉到獨立的函數中。

2.2 管道代替循環

 管道代替循環就是從重構2中獲得的收穫了,之前就一直對管道有好感,而且覺得stream和lambda很神祕,覺得無從下手。所以在智慧營區中還是決定小步前進,不斷的使用小的lambda和stream,以小見大,慢慢對管道進行實踐。
原來,在進行迭代一組集合時,使用循環。但隨着時代的發展,如今越來越多的編程語言提供了更好的語言結構來處理迭代過程,這種結構叫做集合管道(collection pipeline),最常見的爲map和filter莫屬。map運算是指一個函數用作輸入集合的每一個元素上,將集合變換成另一個集合的過程。filter運算是指一個函數從輸入集合中篩選出符合條件的元素子集的過程。運算得到的集合可以供後續的流程使用。Martin Fowler發現,一些邏輯如果採用集合管道來編寫,代碼的可讀性可以更強。只需要從頭到尾閱讀一遍代碼,就能弄清對象在管道中間的變換過程。具體這個過程可以參考文章利用集合管道對循環進行函數式重構認真的理解。

2 lambda實踐

2.1 樣板代碼

下屬的代碼很贊,因爲通過實例來理解stream的使用,也是非常精彩的方式。而且非常可以方便學以致用。

package com.pancm.jdk8;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;



/**
 * @Title: StreamTest
 * @Description: Stream測試用例 流的操作類型分爲兩種:
 * 
 *               Intermediate:一個流可以後面跟隨零個或多個 intermediate
 *               操作。其目的主要是打開流,做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用。
 *               這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷。 Terminal:一個流只能有一個
 *               terminal 操作,當這個操作執行後,流就被使用“光”了,無法再被操作。 所以這必定是流的最後一個操作。 Terminal
 *               操作的執行,纔會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。
 * @Version:1.0.0
 * @author pancm
 * @date 2018年9月3日
 */
public class StreamTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		test1();
		test2();
		test3();
		test4();
		
		/*
		 * Stream 的特性可以歸納爲:
			不是數據結構
			它沒有內部存儲,它只是用操作管道從 source(數據結構、數組、generator function、IO channel)抓取數據。
			它也絕不修改自己所封裝的底層數據結構的數據。例如 Stream 的 filter 操作會產生一個不包含被過濾元素的新 Stream,而不是從 source 刪除那些元素。
			所有 Stream 的操作必須以 lambda 表達式爲參數
			不支持索引訪問
			你可以請求第一個元素,但無法請求第二個,第三個,或最後一個。不過請參閱下一項。
			很容易生成數組或者 List
			惰性化
			很多 Stream 操作是向後延遲的,一直到它弄清楚了最後需要多少數據纔會開始。
			Intermediate 操作永遠是惰性化的。
			並行能力
			當一個 Stream 是並行化的,就不需要再寫多線程代碼,所有對它的操作會自動並行進行的。
			可以是無限的
			集合有固定大小,Stream 則不必。limit(n) 和 findFirst() 這類的 short-circuiting 操作可以對無限的 Stream 進行運算並很快完成。
		 */
	}

	/**
	 * 簡單實用
	 */
	private static void test1() {
		/*
		 * 普通的方式過濾
		 */
		List<String> list = Arrays.asList("張三", "李四", "王五", "xuwujing");
		System.out.println("過濾之前:" + list);
		List<String> result = new ArrayList<>();
		for (String str : list) {
			if (!"李四".equals(str)) {
				result.add(str);
			}
		}
		System.out.println("過濾之後:" + result);

		/*
		 * stream 過濾
		 */
		List<String> result2 = list.stream().filter(str -> !"李四".equals(str)).collect(Collectors.toList());
		System.out.println("stream 過濾之後:" + result2);
		// 另一種方式輸出
		result2.forEach(System.out::println);

		// 使用stream.filter ()過濾一列表,並.findAny().orElse
		// 遍歷該list,查詢數據,如果查不到,就返回 找不到!
		String result3 = list.stream().filter(str -> "李四".equals(str)).findAny().orElse("找不到!");
		String result4 = list.stream().filter(str -> "李二".equals(str)).findAny().orElse("找不到!");

		System.out.println("stream 過濾之後 2:" + result3);
		System.out.println("stream 過濾之後 3:" + result4);
		//stream 過濾之後 2:李四
		//stream 過濾之後 3:找不到!
	}

	/**
	 * 基本使用
	 */
	@SuppressWarnings({ "unchecked", "rawtypes", "unused" })
	private static void test2() {

		/*
		 * 構造流的幾種方式
		 */
		Stream stream = Stream.of("a", "b", "c");
		String[] strArray = new String[] { "a", "b", "c" };
		stream = Stream.of(strArray);
		stream = Arrays.stream(strArray);
		List<String> list = Arrays.asList(strArray);
		stream = list.stream();

		/*
		 * 流之間的相互轉化 一個 Stream 只可以使用一次,這段代碼爲了簡潔而重複使用了數次,因此會拋出異常
		 */
		try {
			Stream<String> stream2 = Stream.of("a", "b", "c");
			// 轉換成 Array
			String[] strArray1 = stream2.toArray(String[]::new);

			// 轉換成 Collection
			List<String> list1 = stream2.collect(Collectors.toList());
			List<String> list2 = stream2.collect(Collectors.toCollection(ArrayList::new));			
			Set set1 = stream2.collect(Collectors.toSet());
			Stack stack1 = stream2.collect(Collectors.toCollection(Stack::new));

			// 轉換成 String
			String str = stream.collect(Collectors.joining()).toString();
		} catch (Exception e) {
			e.printStackTrace();
		}

		/*
		 * 彙總操作
		 */
		List<User> lists = new ArrayList<User>();
		lists.add(new User(6, "張三"));
		lists.add(new User(2, "李四"));
		lists.add(new User(3, "王五"));
		lists.add(new User(1, "張三"));
		// 計算這個list中出現 "張三" id的值
		int sum = lists.stream().filter(u -> "張三".equals(u.getName())).mapToInt(u -> u.getId()).sum();

		System.out.println("計算結果:" + sum); 
		// 7

		/*
		 * 數值類型的流 包括IntStream, LongStream和DoubleStream
		 */
		System.out.println("遍歷輸出該數組的數據:");
		IntStream.of(new int[] { 1, 2, 3, 4 }).forEach(System.out::println);
		System.out.println("查詢範圍在 2-3(2<=i<3)之間的數據:");
		IntStream.range(2, 3).forEach(System.out::println);
		System.out.println("查詢範圍在2-3(2<=i<=3)之間的數據:");
		IntStream.rangeClosed(2, 3).forEach(System.out::println);

		/* stream中的 map使用 */

		/*
		 * 轉換大寫
		 */
		List<String> list3 = Arrays.asList("zhangSan", "liSi", "wangWu");
		System.out.println("轉換之前的數據:" + list3);
		List<String> list4 = list3.stream().map(String::toUpperCase).collect(Collectors.toList());
		System.out.println("轉換之後的數據:" + list4); 
		// 轉換之後的數據:[ZHANGSAN, LISI,WANGWU]
		
		/*
		 * 轉換數據類型
		 */
		List<String> list31 = Arrays.asList("1", "2", "3");
		System.out.println("轉換之前的數據:" + list31);
		List<Integer> list41 = list31.stream().map(Integer::valueOf).collect(Collectors.toList());
		System.out.println("轉換之後的數據:" + list41); 
		// [1, 2, 3]
		
		
		/*
		 *  轉換數據類型
		 *  對象轉map
		 */
		List<User> list32 = new ArrayList<User>();
		for(int i=1;i<=10;i++){
			list32.add(new User(i,"張三"+i));
		}
		
		System.out.println("轉換之前的數據:" + list32);// 轉換之前的數據:[1, 2, 3]
		List<Map> list42 = list32.stream().map(User::toMap).collect(Collectors.toList());
		System.out.println("轉換之後的數據:" + list42); // [1, 2, 3]
		
		
		/*
		 * 獲取平方
		 */
		List<Integer> list5 = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5 });
		List<Integer> list6 = list5.stream().map(n -> n * n).collect(Collectors.toList());
		System.out.println("平方的數據:" + list6);
		// [1, 4, 9, 16, 25]

		/*
		 * flatMap 一對多 得到多個數組裏面的數字
		 */
		Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
		Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());
		System.out.println("打印 stream中的數字:");
		outputStream.forEach(System.out::println);

		/*
		 * 得到一段句子中的單詞
		 */
		String worlds = "The way of the future";
		List<String> list7 = new ArrayList<>();
		list7.add(worlds);
		List<String> list8 = list7.stream().flatMap(str -> Stream.of(str.split(" ")))
				.filter(world -> world.length() > 0).collect(Collectors.toList());
		System.out.println("單詞:");
		list8.forEach(System.out::println);
		// 單詞:
		// The 
		// way 
		// of 
		// the 
		// future
		
		/*
		 * peek 對每個元素執行操作並返回一個新的 Stream
		 */
		System.out.println("peek使用:");
		Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("轉換之前: " + e))
				.map(String::toUpperCase).peek(e -> System.out.println("轉換之後: " + e)).collect(Collectors.toList());
		
		//	轉換之前: three
		//	轉換之後: THREE
		//	轉換之前: four
		//	轉換之後: FOUR
		
		
		
		/*
		 * limit 和 skip limit 返回 Stream 的前面 n 個元素;skip 則是扔掉前 n 個元素(它是由一個叫
		 * subStream 的方法改名而來)。
		 */
		
		//limit 簡單使用
		Random rd = new Random();
		System.out.println("取到的前三條數據:");
		rd.ints().limit(3).forEach(System.out::println);
		//	取到的前三條數據:
		//	1167267754
		//	-1164558977
		//	1977868798
		
		List<User> list9 = new ArrayList<User>();
		for (int i = 1; i < 4; i++) {
			User user = new User(i, "pancm" + i);
			list9.add(user);
		}
		System.out.println("截取之前的數據:");
		// 取前3條數據,但是扔掉了前面的2條,可以理解爲拿到的數據爲 2<=i<3 (i 是數值下標)
		List<String> list10 = list9.stream().map(User::getName).limit(3).skip(2).collect(Collectors.toList());
		System.out.println("截取之後的數據:" + list10);
		//		截取之前的數據:
		//		姓名:pancm1
		//		姓名:pancm2
		//		姓名:pancm3
		//		截取之後的數據:[pancm3]
		
		
		/*
		 * sort 進行排序 先獲取在排序效率更高
		 */
		
		Random rd2 = new Random();
		System.out.println("取到的前三條數據然後進行排序:");
		rd2.ints().limit(3).sorted().forEach(System.out::println);
		//	取到的前三條數據然後進行排序:
		//	-2043456377
		//	-1778595703
		//	1013369565
		
		//普通的排序取值
		List<User> list11 = list9.stream().sorted((u1, u2) -> u1.getName().compareTo(u2.getName())).limit(3)
				.collect(Collectors.toList());
		System.out.println("排序之後的數據:" + list11);
		//優化排序取值
		List<User> list12 = list9.stream().limit(3).sorted((u1, u2) -> u1.getName().compareTo(u2.getName()))
				.collect(Collectors.toList());
		System.out.println("優化排序之後的數據:" + list12);
		//排序之後的數據:[{"id":1,"name":"pancm1"}, {"id":2,"name":"pancm2"}, {"id":3,"name":"pancm3"}]
		//優化排序之後的數據:[{"id":1,"name":"pancm1"}, {"id":2,"name":"pancm2"}, {"id":3,"name":"pancm3"}]
		
		/*
		 * min/max/distinct
		 * 最大,最小和去重
		 */
		
		List<String> list13 = Arrays.asList("zhangsan","lisi","wangwu","xuwujing");
		int maxLines = list13.stream().mapToInt(String::length).max().getAsInt();
		int minLines = list13.stream().mapToInt(String::length).min().getAsInt();
		System.out.println("最長字符的長度:" + maxLines+",最短字符的長度:"+minLines);
		//最長字符的長度:8,最短字符的長度:4
		
		String lines = "good good study day day up";
		List<String> list14 = new ArrayList<String>();
		list14.add(lines);
		List<String> words = list14.stream().flatMap(line -> Stream.of(line.split(" "))).filter(word -> word.length() > 0)
				.map(String::toLowerCase).distinct().sorted().collect(Collectors.toList());
		System.out.println("去重複之後:" + words);
		//去重複之後:[day, good, study, up]

		/*
		 * Match 匹配
		 * 
		 * allMatch:Stream 中全部元素符合則返回 true ;
		 * anyMatch:Stream 中只要有一個元素符合則返回 true; 
		 * noneMatch:Stream 中沒有一個元素符合則返回 true。
		 */

		boolean all = lists.stream().allMatch(u -> u.getId() > 3);
		System.out.println("是否都大於3:" + all);
		boolean any = lists.stream().anyMatch(u -> u.getId() > 3);
		System.out.println("是否有一個大於3:" + any);
		boolean none = lists.stream().noneMatch(u -> u.getId() > 3);
		System.out.println("是否沒有一個大於3的:" + none);		
		//	是否都大於3:false
		//	是否有一個大於3:true
		//	是否沒有一個大於3的:false
		
		/*
		 * 生成隨機數 通過實現 Supplier 接口,你可以自己來控制流的生成。這種情形通常用於隨機數、常量的
		 * Stream,或者需要前後元素間維持着某種狀態信息的 Stream。 把 Supplier 實例傳遞給 Stream.generate()
		 * 生成的 Stream,默認是串行(相對 parallel 而言)但無序的(相對 ordered 而言)。
		 * 由於它是無限的,在管道中,必須利用 limit 之類的操作限制 Stream 大小。
		 */
		Random seed = new Random();
		seed.ints().limit(3).forEach(System.out::println);
		Supplier<Integer> random = seed::nextInt;
		System.out.println("生成5個隨機數:");
		Stream.generate(random).limit(3).forEach(System.out::println);
		System.out.println("生成5正整數的隨機數:");
		IntStream.generate(() -> (int) (System.nanoTime() % 100)).limit(3).forEach(System.out::println);
		System.out.println("生成5個隨機數:");
	
		
		/*
		 並行(parallel)程序
		parallelStream 是流並行處理程序的代替方法。
		 */
		List<String> strings = Arrays.asList("a", "", "c", "", "e","", " ");
		// 獲取空字符串的數量
		long count =  strings.parallelStream().filter(string -> string.isEmpty()).count();
		System.out.println("空字符串的個數:"+count);
		
		
		/*
		 * 合併字符串
		 */
		List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
		System.out.println("篩選列表: " + filtered);
		String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
		System.out.println("合併字符串: " + mergedString);
		//	篩選列表: [a, c, e,  ]
		//	合併字符串: a, c, e,  
		
	}

	/**
	 * 一些關聯使用
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static void test3() {

		/*
		 * Optional
		 */
		String strA = " abcd ", strB = null;
		System.out.println("數據校驗開始...");
		print(strA);
		print("");
		print(strB);
		getLength(strA);
		getLength("");
		getLength(strB);
		System.out.println("數據校驗結束...");

		/*
		 * reduce 主要作用是把 Stream 元素組合起來。
		 */
		// 字符串連接,concat = "ABCD"
		String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
		System.out.println("字符串拼接:" + concat);
		//字符串拼接:ABCD
		// 求最小值
		double minValue = Stream.of(-4.0, 1.0, 3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
		System.out.println("最小值:" + minValue);
		//最小值:-4.0
		
		
		// 求和, 無起始值
		int sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
		System.out.println("有無起始值求和:" + sumValue);
		// 求和, 有起始值
		 sumValue = Stream.of(1, 2, 3, 4).reduce(1, Integer::sum);
		System.out.println("有起始值求和:" + sumValue);
		//	有無起始值求和:10
		//	有起始值求和:11
		
		
		// 過濾,字符串連接,concat = "ace"
		concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
		System.out.println("過濾和字符串連接:" + concat);
		//過濾和字符串連接:ace
		
		
		
		/*
		 * iterate iterate 跟 reduce 操作很像,接受一個種子值,和一個 UnaryOperator(例如 f)。
		 * 然後種子值成爲 Stream 的第一個元素,f(seed) 爲第二個,f(f(seed)) 第三個,以此類推。 在 iterate
		 * 時候管道必須有 limit 這樣的操作來限制 Stream 大小。
		 */
		System.out.println("從2開始生成一個等差隊列:");
		Stream.iterate(2, n -> n + 2).limit(5).forEach(x -> System.out.print(x + " "));
		// 從2開始生成一個等差隊列:
		// 2 4 6 8 10
		
		
		System.out.println("\n");
		/*
		 * 分組排序 groupingBy/partitioningBy
		 */
		// 通過id進行排序
		System.out.println("通過id進行分組排序:");
		Map<Integer, List<User>> personGroups = Stream.generate(new UserSupplier2()).limit(5)
				.collect(Collectors.groupingBy(User::getId));
		Iterator it = personGroups.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry<Integer, List<User>> persons = (Map.Entry) it.next();
			System.out.println("id " + persons.getKey() + " = " + persons.getValue());
		}
		
		//	通過id進行分組排序:
		//	id 10 = [{"id":10,"name":"pancm1"}]	
		//	id 11 = [{"id":11,"name":"pancm3"}, {"id":11,"name":"pancm6"}, {"id":11,"name":"pancm4"}, {"id":11,"name":"pancm7"}]

		
		
		//通過年齡排序
		System.out.println("通過年齡進行分區排序:");
		Map<Boolean, List<User>> children = Stream.generate(new UserSupplier3()).limit(5)
				.collect(Collectors.partitioningBy(p -> p.getId() < 18));

		System.out.println("小孩: " + children.get(true));
		System.out.println("成年人: " + children.get(false));
		
		// 通過年齡進行分區排序:
		// 小孩: [{"id":16,"name":"pancm7"}, {"id":17,"name":"pancm2"}]
		// 成年人: [{"id":18,"name":"pancm4"}, {"id":19,"name":"pancm9"}, {"id":20,"name":"pancm6"}]
		
		
		
		/*
		 *  IntSummaryStatistics 用於收集統計信息(如count、min、max、sum和average)的狀態對象。
		 */
		List<Integer> numbers = Arrays.asList(1, 5, 7, 3, 9);
		IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
		 
		System.out.println("列表中最大的數 : " + stats.getMax());
		System.out.println("列表中最小的數 : " + stats.getMin());
		System.out.println("所有數之和 : " + stats.getSum());
		System.out.println("平均數 : " + stats.getAverage());
		
		//	列表中最大的數 : 9
		//	列表中最小的數 : 1
		//	所有數之和 : 25
		//	平均數 : 5.0
		
	}

	/**
	 * 自定義流
	 */
	private static void test4() {

		/*
		 * 自定義一個流 然後進行輸出
		 */
		System.out.println("自定義一個流進行計算輸出:");
		Stream.generate(new UserSupplier()).limit(2).forEach(u -> System.out.println(u.getId() + ", " + u.getName()));
		
		//第一次:
		//自定義一個流進行計算輸出:
		//10, pancm7
		//11, pancm6
		
		//第二次:
		//自定義一個流進行計算輸出:
		//10, pancm4
		//11, pancm2
		
		//第三次:
		//自定義一個流進行計算輸出:
		//10, pancm4
		//11, pancm8
	}

	public static void print(String text) {
		// jdk1.8之前的寫法
		// if (text != null) {
		// System.out.println(text);
		// }
		// jdk1.8的寫法
		Optional.ofNullable(text).ifPresent(System.out::println);
	}

	public static void getLength(String text) {
		// jdk1.8之前的寫法
		// return if (text != null) ? text.length() : -1;
		// jdk1.8的寫法
		int i = Optional.ofNullable(text).map(String::length).orElse(-1);
		System.out.println("數據:" + i);
	};
}

class UserSupplier implements Supplier<User> {
	private int index = 10;
	private Random random = new Random();

	@Override
	public User get() {
		return new User(index++, "pancm" + random.nextInt(10));
	}
}

class UserSupplier2 implements Supplier<User> {
	private int index = 10;
	private Random random = new Random();

	@Override
	public User get() {
		return new User(index % 2 == 0 ? index++ : index, "pancm" + random.nextInt(10));
	}
}

class UserSupplier3 implements Supplier<User> {
	private int index = 16;
	private Random random = new Random();

	@Override
	public User get() {
		return new User(index++, "pancm" + random.nextInt(10));
	}
}

具體內容可以參考github項目java學習項目

2.2 智慧營區項目中實踐

2.2.1 主機端口獲取

通過Arrays.asList()把多個同類型的成員組織成List,進而獲取stream

public static String getHostPort(String ip, String port) {
        List<String> urlElements = Arrays.asList(HTTP_PROCTOL, ip, ":", port);
        return urlElements.stream().collect(Collectors.joining(""));
    }

這主要是因爲在ServerConfig類中出現了很多的重複模式

"http" + ip +":" +port

因此可以使用getHostPort函數進行重構,如下所示:

package com.cetc52.camp.config;

import com.cetc52.camp.util.GeneralUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 獲取項目的IP和端口
 *
 * @author songquanheng
 * @Time: 2019/2/22-16:21
 */

@Component
@Data
public class ServerConfig {
    @Value("${retrieval.ip}")
    private String retrievalIp;
    @Value("${retrieval.port}")
    private String retrievalPort;

    @Value("${camp.ip}")
    private String serverIp;
    @Value("${camp.port}")
    private String serverPort;


    @Value("${inferEngine.ip}")
    private String inferEngineIp;
    @Value("${inferEngine.port}")
    private String inferEnginePort;

    public String getRetrievalUrl() {
        return GeneralUtil.getHostPort(retrievalIp, retrievalPort);
    }

    public String getServerUrl() {
        return GeneralUtil.getHostPort(serverIp, serverPort);
    }

    public String getInferEngineUrl() {
        return GeneralUtil.getHostPort(inferEngineIp, inferEnginePort);
    }
}

重複代碼(Duplicate Code),如果你在一個以上的地點看到相同的代碼結構,那麼可以肯定:設法將它們合二爲一,程序會變得更好。一旦有重複的代碼存在,閱讀這些重複的代碼時,你就必須加倍仔細,留意其間細微的差異。如果要修改重複的代碼時,你就必須找出所有的副本來修改(重構第二版p72)。在Clean Code 一書中,Robert也是如此編寫的。程序應該遵守DRY原則,重複的是罪惡的源泉,遵守DRY: Donot Repeat Yourself原則,發現代碼中的重複之處,這樣你會漸漸發現程序的質量逐漸就會有質的提升。
下述代碼爲使用代碼重構之前的形式:

package com.cetc52.camp.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 獲取項目的IP和端口
 *
 * @author songquanheng
 * @Time: 2019/2/22-16:21
 */

@Component
@Data
public class ServerConfig {
    public static final String HTTP_PROCTOL = "http://";

    @Value("${retrieval.ip}")
    private String retrievalIp;
    @Value("${retrieval.port}")
    private String retrievalPort;

    @Value("${camp.ip}")
    private String serverIp;
    @Value("${camp.port}")
    private String serverPort;


    @Value("${inferEngine.ip}")
    private String inferEngineIp;
    @Value("${inferEngine.port}")
    private String inferEnginePort;

    public String getRetrievalUrl() {
        return HTTP_PROCTOL + retrievalIp + ":" + retrievalPort;
    }

    public String getServerUrl() {
        return HTTP_PROCTOL + serverIp + ":" + serverPort;
    }

    public String getInferEngineUrl() {
        return HTTP_PROCTOL + inferEngineIp + ":" + inferEnginePort;
    }

}

可以看到通過更積極的分解函數,代碼變得更加凝練,而且出錯的概率更小,因爲寫出的函數已經得到了很好的測試,在撰寫代碼時,你會更加自信,代碼也會變得更加優雅。

2.2.2 定時任務修改map的實踐,並且移除map中key-value

private static Map<String, AreaInvade> areaInvadeMap = new HashMap<>();
    private static Map<String, AreaRetention> areaRetentionMap = new HashMap<>();
    private static Map<String, CrossBorder> crossBorderMap = new HashMap<>();
    private static ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
            new BasicThreadFactory.Builder().namingPattern("object-detection-pool-%d").daemon(true).build());
    /**
     * 區域入侵分析器
     */
    private AreaInvade areaInvade;
    /**
     * 徘徊偵測分析器
     */
    private AreaRetention areaRetention;
    /**
     * 跨越邊界分析器
     */
    private CrossBorder crossBorder;

    static {
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            cancelDeletedTaskAnalyzer();
            removeDeletedTaskAnalyzer();
            cancelNotRunningTaskAnalyzer();
            removeNotRunningTaskAnalyzer();
        }, 10, 5, TimeUnit.SECONDS);
    }


    /**
     * 應對安防任務終止即刪除的情形。取消已經刪除的安防任務分析器中定時任務以及保存的人車物id數據集合。
     */
    public static void cancelDeletedTaskAnalyzer() {
        areaInvadeMap.forEach((actualTaskId, areaInvade) -> {
            if (taskHasBeenDeleted(actualTaskId)) {
                log.info("取消一個已經被刪除的安防任務的區域入侵分析器");
                areaInvade.getTimer().cancel();
                areaInvade.clear();
            }
        });

        areaRetentionMap.forEach((actualTaskId, areaRetention) -> {
            if (taskHasBeenDeleted(actualTaskId)) {
                log.info("取消一個已經被刪除的安防任務的區域徘徊偵測分析器");
                areaRetention.getTimer().cancel();
                areaRetention.clear();
            }
        });

        crossBorderMap.forEach((actualTaskId, crossBorder) -> {
            if (taskHasBeenDeleted(actualTaskId)) {
                log.info("取消一個已經被刪除的安防任務的越界偵測分析器");
                crossBorder.getTimer().cancel();
                crossBorder.clear();
            }

        });

    }

    /**
     * 應對安防任務終止即終止立即刪除任務的情形。取消已經刪除的安防任務分析器。
     * 此時定時任務5秒之後運行根據actual_task_id獲取可能獲取不到,返回null的問題。
     */
    public static void removeDeletedTaskAnalyzer() {
        areaInvadeMap.keySet().removeIf(actualTaskId -> taskHasBeenDeleted(actualTaskId));
        areaRetentionMap.keySet().removeIf(actualTaskId -> taskHasBeenDeleted(actualTaskId));
        areaRetentionMap.keySet().removeIf(actualTaskId -> taskHasBeenDeleted(actualTaskId));

    }

    private static boolean taskHasBeenDeleted(String actualTaskId) {
        List<String> actualTaskIds = allTaskEntitiesFromDataBase();
        return !actualTaskIds.contains(actualTaskId);
    }

    private static List<String> allTaskEntitiesFromDataBase() {
        List<TaskEntity> taskEntities = taskMapper.selectAll();
        return taskEntities.stream().map(taskEntity1 -> taskEntity1.getActualTaskId()).collect(Collectors.toList());
    }

    /**
     * 取消一個安防分析器的定時任務並清空其中保存的人、車、物id
     */
    private static void cancelNotRunningTaskAnalyzer() {
        areaInvadeMap.forEach((actualTaskId, areaInvade) -> {
            if (taskNotRunning(taskMapper.selectByTaskId(actualTaskId))) {
                log.info("取消一個區域入侵分析器");
                areaInvade.getTimer().cancel();
                areaInvade.clear();
            }
        });

        areaRetentionMap.forEach((actualTaskId, areaRetention) -> {
            if (taskNotRunning(taskMapper.selectByTaskId(actualTaskId))) {
                log.info("取消一個區域徘徊偵測分析器");
                areaRetention.getTimer().cancel();
                areaRetention.clear();
            }
        });

        crossBorderMap.forEach((actualTaskId, crossBorder) -> {
            if (taskNotRunning(taskMapper.selectByTaskId(actualTaskId))) {
                log.info("取消一個越界偵測分析器");
                crossBorder.getTimer().cancel();
                crossBorder.clear();
            }

        });
    }

    /**
     * 移除不再運行的安防分析器
     */
    private static void removeNotRunningTaskAnalyzer() {
        areaInvadeMap.keySet().removeIf(actualTaskId -> taskNotRunning(taskMapper.selectByTaskId(actualTaskId)));
        areaRetentionMap.keySet().removeIf(actualTaskId -> taskNotRunning(taskMapper.selectByTaskId(actualTaskId)));
        crossBorderMap.keySet().removeIf(actualTaskId -> taskNotRunning(taskMapper.selectByTaskId(actualTaskId)));
    }

    private static boolean taskNotRunning(TaskEntity taskEntity) {
        return !TaskStatus.RUNNING.getValue().equalsIgnoreCase(taskEntity.getTaskStatus());
    }

從最早的編程語言開始,循環就一直是程序設計的核心要素。但我已經開始漸漸鄙視循環語句,如今,函數已經作爲一等公民得到了廣泛的支持,因此我們可以對使用管道替換循環來讓這些循環退休。並且保證循環語句也遵循單一工作。即做一件事,也只做一件事。
上述的代碼中,前三條語句用於更新定時任務中的areaRetentionMap所持有的areaRetention分析器的定時任務和持有的物品映射集合。後三條語句用於使用lambda表達式移除不再處於運行狀態的taskEntity的areaRentention。
關於map中刪除key-value可以通過下面的代碼片段理解:

// 通過value移除
map.values().removeIf(value -> !value.contains("1"));
// 通過key移除
map.keySet().removeIf(key -> key != 1);
// 通過鍵/值的輸入/組合刪除
map.entrySet().removeIf(entry -> entry.getKey() != 1);

具體理解可以參考Java刪除Map中元素

2.2.3 字符串合併

/**
     * 用於合併字符串
     * @param elements 用於合併字符串,默認分隔符爲空""
     * @return 返回合併後的字符串
     */
    public static String mergeString(String... elements) {
        return Arrays.stream(elements).collect(Collectors.joining(StringUtils.EMPTY));
    }

此處與getHostPort函數類似,不再贅述。不過使用了可變字符串參數。可變參數實踐可以參見Arrays.asList()函數。

/**
     * Returns a fixed-size list backed by the specified array.  (Changes to
     * the returned list "write through" to the array.)  This method acts
     * as bridge between array-based and collection-based APIs, in
     * combination with {@link Collection#toArray}.  The returned list is
     * serializable and implements {@link RandomAccess}.
     *
     * <p>This method also provides a convenient way to create a fixed-size
     * list initialized to contain several elements:
     * <pre>
     *     List&lt;String&gt; stooges = Arrays.asList("Larry", "Moe", "Curly");
     * </pre>
     *
     * @param <T> the class of the objects in the array
     * @param a the array by which the list will be backed
     * @return a list view of the specified array
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

需要注意,可變長參數應該爲函數的最後一個參數,而且若只有兩個參數,則另一個參數不能與可變長參數的類型相同。在編寫合併字符串時,筆者曾想要賦予mergeString函數一個delimiter的參數,用於指定分隔符。結果發現了這個問
題。

2.2.4 組合JSONObject

/**
     * 待存儲到檢索平臺的檢測識別結果
     *
     * @param recognitionResult 一條檢索結果
     * @param bgAndTime         背景大圖的信息與發生時間。
     * @param deviceInfo        設備信息,主要爲設備id,設備名稱
     * @return 返回存儲到檢索平臺的檢測識別結果
     */
    protected JSONObject organizeRetrievalResult(JSONObject recognitionResult,
                                                 JSONObject bgAndTime, JSONObject deviceInfo) {
        JSONObject result = JSONObject.parseObject(recognitionResult.toJSONString());
        bgAndTime.forEach((k, v) -> {
            result.put(k, v);
        });
        deviceInfo.forEach((k, v) -> {
            result.put(k, v);
        });
        return result;
    }

2.2.5 異步發送事件

 /**
     * @param eventReport 上報參數
     * @param taskType    任務類型
     * @description 接受上報數據
     */
    @Async
    public void reportIntelligentAnalysis(String taskType, JSONObject eventReport) {
        log.info("Enter reportIntelligentAnalysis");
        log.info("發生:" + taskType + "事件,上報數據" + eventReport.toJSONString());
        sendEvent(eventReport);
        List<String> urls = uploadUrlManagerService.selectByTaskType(taskType);

        urls.stream().forEach(url -> {
            send(url, eventReport);
        });
        log.info("Leave reportIntelligentAnalysis");
    }

2.3 實例闡述

Stream作爲java8的新特性,基於lambda表達式,是對集合對象功能的增強,它專注於對集合對象進行各種高效、便利的聚合操作或者大批量的數據操作,提高了編程效率和代碼的可讀性。
 Stream的原理:將要處理的元素看作一種流,流在管道中傳輸,並且可以在管道的節點上進行處理,包括過濾篩選、去重、排序、聚合。元素流在管道中經過中間操作的處理,最終由最終操作得到前面處理的結果。
中間操作:
中間操作主要有以下方法(此類型方法返回的都是 Stream):map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

終止操作主要有以下方法:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

2.3.1 演示使用model

package com.example.profile.model;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Owner: SongQuanHeng
 * @Time: 2019/10/19-11:20
 * @Version:
 * @Change:
 */
public class Student {

    private Long id;

    private  String name;

    private int age;

    private String address;

    public Student() {
    }

    public Student(Long id, String name,int age,String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{"+"id="+id+
                ", name="+name+", age="+age+", address="+address+"}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(id, student.id) &&
                Objects.equals(name, student.name) &&
                Objects.equals(address, student.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, address);
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public static void main(String[] args) {

        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(3L, "宋信", 50, "河北");
        Student s4 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        System.out.println(students.stream().filter(s -> s.getAge() > 20).collect(Collectors.toList()));
        List<Student> streamStudents = testFilter(students);
        streamStudents.forEach(System.out::println);
    }

/**

 * 集合的篩選

 * @param students

 * @return

 */

    private static List<Student> testFilter(List<Student> students) {

//篩選年齡大於15歲的學生

//        return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList());

//篩選住在浙江省的學生

        return students.stream().filter(s -> "浙江".equals(s.getAddress())).collect(Collectors.toList());

    }

}

2.3.2 數據準備

public static void main(String[] args) {

        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(3L, "宋信", 50, "河北");
        Student s4 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        System.out.println(students.stream().filter(s -> s.getAge() > 20).collect(Collectors.toList()));
        List<Student> streamStudents = testFilter(students);
        streamStudents.forEach(System.out::println);
    }

/**

 * 集合的篩選

 * @param students

 * @return

 */

    private static List<Student> testFilter(List<Student> students) {

//篩選年齡大於15歲的學生

//        return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList());

//篩選住在浙江省的學生

        return students.stream().filter(s -> "浙江".equals(s.getAddress())).collect(Collectors.toList());

    }

輸出如下:

[Student{id=3, name=宋信, age=50, address=河北}, Student{id=4, name=王菲, age=28, address=浙江}]
Student{id=1, name=肖戰, age=15, address=浙江}
Student{id=4, name=王菲, age=28, address=浙江}

上述代碼演示了過濾的用途,最終操作是形成一個新的List。

2.3.3 map轉換

2.3.3.1 項目實踐

private static List<String> allTaskEntitiesFromDataBase() {
        List<TaskEntity> taskEntities = taskMapper.selectAll();
        return taskEntities.stream().map(taskEntity1 -> taskEntity1.getActualTaskId()).collect(Collectors.toList());
    }

上述的代碼邏輯是把List轉換成了List

2.3.3.2 map實例演示

Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(3L, "宋信", 50, "河北");
        Student s4 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s4);
        List<String> addresses = students.stream().map(student -> "住址: "+student.getAddress()).collect(Collectors.toList());
        addresses.forEach(s->System.out.println(s));

演示結果如下:

住址: 浙江
住址: 四川
住址: 河北
住址: 浙江

2.3.4 去重

2.3.4.1 基礎類型去重

List<String> list = Arrays.asList("111", "222", "333", "111", "222");
        list.stream().distinct().forEach(System.out::println);

演示結果如下:

111
222
333

2.3.4.2 引用對象去重

Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(2L, "周雲", 18, "四川");
        Student s5 = new Student(4L, "王菲", 28, "浙江");
        Student s6 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s5);
        students.add(s6);
        students.stream().distinct().forEach(System.out::println);

演示結果如下:

Student{id=1, name=肖戰, age=15, address=浙江}
Student{id=2, name=周雲, age=18, address=四川}
Student{id=4, name=王菲, age=28, address=浙江}

注意:引用對象的去重要實現hashCode和equals,否則去重無效。

2.3.5 sorted排序

2.3.5.1 基礎類型排序

        List<String> list1 = Arrays.asList("333", "555", "222", "444", "111");
        list1.stream().sorted().forEach(System.out::println);

演示結果:

111
222
333
444
555

2.3.5.2 引用類型排序

        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(2L, "周雲", 10, "四川");
        Student s5 = new Student(4L, "王菲", 28, "浙江");
        Student s6 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s5);
        students.add(s6);

        students.stream()
                .sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
                .forEach(System.out::println);
        System.out.println("=====================");

        students.stream()
                .sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
                .sorted((student1, student2)->Integer.compare(student1.getAge(), student2.getAge()))
                .forEach(System.out::println);

演示結果如下:

Student{id=1, name=肖戰, age=15, address=浙江}
Student{id=2, name=周雲, age=18, address=四川}
Student{id=2, name=周雲, age=10, address=四川}
Student{id=4, name=王菲, age=28, address=浙江}
Student{id=4, name=王菲, age=28, address=浙江}
=====================
Student{id=2, name=周雲, age=10, address=四川}
Student{id=1, name=肖戰, age=15, address=浙江}
Student{id=2, name=周雲, age=18, address=四川}
Student{id=4, name=王菲, age=28, address=浙江}
Student{id=4, name=王菲, age=28, address=浙江}

2.3.6 limit(限制返回個數)

List<String> list1 = Arrays.asList("333", "555", "222", "444", "111");
        list1.stream().limit(3).forEach(System.out::println);
        System.out.println("limit=====================");

演示結果:

333
555
222
limit=====================

2.3.7 skip(刪除元素)

List<String> list2 = Arrays.asList("111", "222", "333", "111", "222");
        list2.stream().skip(2).forEach(System.out::println);

演示結果:

333
111
222

2.3.8 reduce(聚合)

List<String> list3 = Arrays.asList("歡", "迎", "你");
        String appendStr = list3.stream().reduce("北京", (a, b) -> a + b);
        System.out.println(appendStr);

演示結果

北京歡迎你

關於reduce函數,註解如下

/**
     * Performs a <a href="package-summary.html#Reduction">reduction</a> on the
     * elements of this stream, using the provided identity value and an
     * <a href="package-summary.html#Associativity">associative</a>
     * accumulation function, and returns the reduced value.  This is equivalent
     * to:
     * <pre>{@code
     *     T result = identity;
     *     for (T element : this stream)
     *         result = accumulator.apply(result, element)
     *     return result;
     * }</pre>
     *
     * but is not constrained to execute sequentially.
     *
     * <p>The {@code identity} value must be an identity for the accumulator
     * function. This means that for all {@code t},
     * {@code accumulator.apply(identity, t)} is equal to {@code t}.
     * The {@code accumulator} function must be an
     * <a href="package-summary.html#Associativity">associative</a> function.
     *
     * <p>This is a <a href="package-summary.html#StreamOps">terminal
     * operation</a>.
     *
     * @apiNote Sum, min, max, average, and string concatenation are all special
     * cases of reduction. Summing a stream of numbers can be expressed as:
     *
     * <pre>{@code
     *     Integer sum = integers.reduce(0, (a, b) -> a+b);
     * }</pre>
     *
     * or:
     *
     * <pre>{@code
     *     Integer sum = integers.reduce(0, Integer::sum);
     * }</pre>
     *
     * <p>While this may seem a more roundabout way to perform an aggregation
     * compared to simply mutating a running total in a loop, reduction
     * operations parallelize more gracefully, without needing additional
     * synchronization and with greatly reduced risk of data races.
     *
     * @param identity the identity value for the accumulating function
     * @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
     *                    <a href="package-summary.html#NonInterference">non-interfering</a>,
     *                    <a href="package-summary.html#Statelessness">stateless</a>
     *                    function for combining two values
     * @return the result of the reduction
     */
    T reduce(T identity, BinaryOperator<T> accumulator);

2.3.9 min(求最小值)

Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(2L, "周雲", 10, "四川");
        Student s5 = new Student(4L, "王菲", 28, "浙江");
        Student s6 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s5);
        students.add(s6);

        Student minS = students.stream().min((stu1, stu2) -> Integer.compare(stu1.getAge(), stu2.getAge())).get();
        System.out.println(minS);

演示結果:

Student{id=2, name=周雲, age=10, address=四川}

2.3.10 anyMatch/allMatch/noneMatch(匹配)

		Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(2L, "周雲", 10, "四川");
        Student s5 = new Student(4L, "王菲", 28, "浙江");
        Student s6 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s5);
        students.add(s6);


        Boolean anyMatch = students.stream().anyMatch(student -> "浙江".equals(s1.address));
        if (anyMatch) {
            System.out.println("有浙江人");
        }

        Boolean allMatch = students.stream().anyMatch(student -> student.getAge()>=10);
        if (allMatch) {
            System.out.println("所有學生都滿10週歲");
        }

        Boolean noneMatch = students.stream().noneMatch(student -> student.getName()=="陽陽");
        if (noneMatch) {
            System.out.println("沒有叫陽陽的同學");
        }

演示結果如下

有浙江人
所有學生都滿10週歲
沒有叫陽陽的同學

2.3.11 演示代碼

package com.example.profile.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Owner: SongQuanHeng
 * @Time: 2019/10/19-11:20
 * @Version:
 * @Change:
 */
public class Student {

    private Long id;

    private  String name;

    private int age;

    private String address;

    public Student() {
    }

    public Student(Long id, String name,int age,String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    @Override
    public String toString() {
        return "Student{"+"id="+id+
                ", name="+name+", age="+age+", address="+address+"}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(id, student.id) &&
                Objects.equals(name, student.name) &&
                Objects.equals(address, student.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, address);
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public static void main(String[] args) {

        Student s1 = new Student(1L, "肖戰", 15, "浙江");
        Student s2 = new Student(2L, "周雲", 18, "四川");
        Student s3 = new Student(2L, "周雲", 10, "四川");
        Student s5 = new Student(4L, "王菲", 28, "浙江");
        Student s6 = new Student(4L, "王菲", 28, "浙江");


        List<Student> students = new ArrayList<>();

        students.add(s1);
        students.add(s2);
        students.add(s3);
        students.add(s5);
        students.add(s6);


        Boolean anyMatch = students.stream().anyMatch(student -> "浙江".equals(s1.address));
        if (anyMatch) {
            System.out.println("有浙江人");
        }

        Boolean allMatch = students.stream().anyMatch(student -> student.getAge()>=10);
        if (allMatch) {
            System.out.println("所有學生都滿10週歲");
        }

        Boolean noneMatch = students.stream().noneMatch(student -> student.getName()=="陽陽");
        if (noneMatch) {
            System.out.println("沒有叫陽陽的同學");
        }

        System.out.println("=====================");

        Student minS = students.stream().min((stu1, stu2) -> Integer.compare(stu1.getAge(), stu2.getAge())).get();
        System.out.println(minS);
        students.stream()
                .sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
                .forEach(System.out::println);
        System.out.println("=====================");

        students.stream()
                .sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
                .sorted((student1, student2)->Integer.compare(student1.getAge(), student2.getAge()))
                .forEach(System.out::println);
        System.out.println("=====================");

        List<String> list1 = Arrays.asList("333", "555", "222", "444", "111");
        list1.stream().limit(3).forEach(System.out::println);
        System.out.println("limit=====================");

        list1.stream().sorted().forEach(System.out::println);


        System.out.println("sorted=====================");

        students.stream().distinct().forEach(System.out::println);
        List<String> addresses = students.stream().map(student -> "住址: "+student.getAddress()).collect(Collectors.toList());
        addresses.forEach(s->System.out.println(s));
        List<String> list = Arrays.asList("111", "222", "333", "111", "222");
        list.stream().distinct().forEach(System.out::println);

        System.out.println("=====================");
        List<String> list2 = Arrays.asList("111", "222", "333", "111", "222");
        list2.stream().skip(2).forEach(System.out::println);
        System.out.println("=====================");
        List<String> list3 = Arrays.asList("歡", "迎", "你");
        String appendStr = list3.stream().reduce("北京", (a, b) -> a + b);
        System.out.println(appendStr);

        //        System.out.println(students.stream().filter(s -> s.getAge() > 20).collect(Collectors.toList()));
//        List<Student> streamStudents = testFilter(students);
//        streamStudents.forEach(System.out::println);
    }

/**

 * 集合的篩選

 * @param students

 * @return

 */

    private static List<Student> testFilter(List<Student> students) {

//篩選年齡大於15歲的學生

//        return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList());

//篩選住在浙江省的學生

        return students.stream().filter(s -> "浙江".equals(s.getAddress())).collect(Collectors.toList());

    }

}

3 總結

從開始對於lambda表達式與stream好感,但慢慢實踐,再由重構第二版Martin Fowler把使用管道取代循環作爲一個讓我醍醐灌頂的重構手法來闡述,自己在智慧營區項目中也進行了相應的stream實踐,也確實從中體會到了管道的優雅之處。不管怎樣,自己會在以後的編程生活中抵制循環,擁抱管道,不斷錘鍊自己的代碼。
在這裏插入圖片描述

							2019年10月16日00:16:26於運糧河漢庭8350
							2019-10-19 12:50於湖墅新村
							2019-12-02 21:54於湖墅新村

					
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章