首頁 > 科技

Java Lambda表示式和Stream流操作

2021-06-09 12:08:45

Lambda表示式

Lambda是函數式介面的匿名實現類的例項物件,函數式介面使用@FunctionalInterface註解來限制介面只能有一個待實現的方法

格式

  • -> 箭頭操作符
  • ->的左邊是入參,入參的類型可以省略,多個入參需要新增()
  • ->的右邊是Lambda體,就是實現類的方法體,如果只有一行可以省略掉{}

簡寫格式

無參 + 無返回值

() -> System.out.println("xxx");

無參時入參需要寫成(),當方法體只有一行時,可以省略掉{}

有參 + 無返回值

(String s1) -> System.out.println(s1);(String s1, String s2) -> System.out.println(s1 + s2);

入參數據類型省略

省略入參類型,會由編譯器進行類型推斷

(s1) -> System.out.println(s1);(s1, s2) -> System.out.println(s1 + s2);

只有一個入參

s1 -> System.out.println(s1);

有返回值

方法體只有一行,可省略return

i1 -> String.valueOf(i1);

直接方法引用,入參和return都可以省略

String::valueOf

方法體有多行,{}和return都不可省略

i1 -> {  System.out.println(i1);  return String.valueOf(i1);}

Stream流操作

這是一種高階的迭代器,用來操作集合,如查詢和過濾等常見操作

Stream操作的過程分為:創建Stream物件 -> 中間操作 -> 終止操作

知識點:

  1. 當執行了終止操作後,該流就不能再執行中間操作了
  2. 在沒有執行終止操作前,中間操作並不會執行,這個稱為惰性
  3. 中間操作分為:無狀態操作和有狀態操作兩種
  4. 無狀態操作中每個元素都按照操作的順序執行,並不是所有元素先完成一步操作後,再一起進入下一步操作
  5. 有狀態操作需要等待所有元素都執行完之前的中間操作後才會執行,比如元素被分組在不同的執行緒中進行無狀態操作,當遇到排序這個操作時,就需要等待其他執行緒的操作都完成後才能執行排序操作
  6. 序列流呼叫parallel()可變成並行流
  7. 並行流呼叫sequential()可變成序列流
  8. 多次連續呼叫parallel()和sequential(),只會保留最後一次呼叫,原因是呼叫這兩個方法,只會改變當前流的sourceStage.parallel這個屬性

創建流

序列流

int[] nums = {1, 2, 3, 4, 5};// 以下兩個陣列的類型任意Arrays.stream(nums);Stream.of(nums);// 基本類型對應的Stream類.of()方法IntStream.of(nums);// 創建1-9的int流IntStream.range(1, 10).forEach(System.out::println);// 創建1-10的int流IntStream.rangeClosed(1, 10).forEach(System.out::println);

並行流

int[] nums = {1, 2, 3, 4, 5};// 可由序列流得到IntStream.of(nums).parallel();// 由集合得到Arrays.asList(nums).parallelStream();

中間操作

中間操作分為:無狀態和有狀態操作

無狀態

map/mapToXxx,map能否改變元素類型要看map方法的入參

int[] nums = {1, 2, 3, 4, 5};// map對每個元素執行一次操作,不可改變元素的類型,因為此map入參是UnaryOperator<T>IntStream.of(nums).map(it -> it + 1).forEach(System.out::println);// 改變元素類型IntStream.of(nums).mapToObj(it -> String.format("[%d]", it)).forEach(System.out::println);// map可以改變元素類型,因為map入參是Function<T, R>ArrayList<Test> list1 = Lists.newArrayList(new Test(1, "a"), new Test(2, "b"));List<Test2> list2 = list1.parallelStream().map(it -> {  	return new Test2(it.getId());}).collect(Collectors.toList());

flatMap/flatMapToXxx

@Data@AllArgsConstructorpublic static class Test {  	private int id;  	private int[] data;}ArrayList<Test> list1 = Lists.newArrayList(new Test(1, new int[]{1, 2, 3}), new Test(2, new int[]{4, 5, 6}));list1.parallelStream()  // Stream<Test>流        .flatMapToInt(it -> IntStream.of(it.getData()))  // 將data陣列轉成IntStream流  			// 上面執行flatMapToInt之後得到一個完整的IntStream流,接下來可以進行IntStream流的一些操作了        .map(it -> it + 1)        .forEach(System.out::println);

filter

int[] nums = {1, 2, 3, 4, 5};IntStream.of(nums).filter(it -> it > 3).forEach(System.out::println);

peek

int[] nums = {1, 2, 3, 4, 5};IntStream.of(nums).peek(System.out::println).forEach(System.out::println);

有狀態

終止操作

注意:並行流呼叫forEach是不保證順序的

reduce

int[] nums = {1, 2, 3, 4, 5};// reduce第一個參數是初始值,第二個參數是BinaryOperator,輸入兩個數返回相加的結果,然後再和上一次的結果相加,最終得到整個流中所有元素相加的和System.out.println(IntStream.of(nums).reduce(0, Integer::sum));

BigDecimal求和

ArrayList<Test3> list3 = Lists.newArrayList(new Test3(1L, 1, 5.5), new Test3(2L, 2, 3.2));BigDecimal reduce = list3.parallelStream()        .map(it -> BigDecimal.valueOf(it.getPrice()).multiply(BigDecimal.valueOf(it.getQty())))        .reduce(BigDecimal.ZERO, BigDecimal::add);System.out.println(reduce);

執行緒池

並行流預設使用自帶的ForkJoinPool,執行緒數是CPU核心數,可以指定自定義的執行緒池

int[] nums = {1, 2, 3, 4, 5};// 使用預設的ForkJoinPoolIntStream.of(nums).parallel().forEach(it -> {    System.out.println(Thread.currentThread().getName() + ":" + it);});// 自定義一個ForkJoinPoolForkJoinPool pool = new ForkJoinPool(2);pool.submit(() -> {    IntStream.of(nums).parallel().forEach(it -> {        System.out.println(Thread.currentThread().getName() + ":" + it);    });});Thread.currentThread().join();


>作者:飛翔的程式碼
連結:https://juejin.cn/post/6971334003097862158
來源:掘金


IT145.com E-mail:sddin#qq.com