Stream的使用


參考資料地址1: java Steam的使用

參考資料地址2: java Steam基礎練習

參考資料地址3: Java8的Steam流常用方法和總結

參考資料地址4: 萬字詳解 Stream 流式編程,寫代碼也可以很優雅

參考資料地址5: stream流分組

一 Stream簡述

Stream 是一個接口,該接口表示流。

1.1 獲取流的幾種方式

  1. 通過Collection集合(單列集合)調用stream()方法獲取。【根據單列集合獲取】

    Collection中獲取流的方法: Stream stream():獲取集合對應的流。獲取流

  2. 通過Stream中的靜態方法根據數組獲取流。【根據數組獲取】

    在Stream中有一個靜態方法,可以根據數組獲取流。

    Stream中根據數組獲取流的方法: static Stream of(T... values):根據數組或多個元素獲取Stream流。

  3. Arrays.stream(T array)

  4. 通過 Stream.builder() 創建: Stream

  5. 通過生成器創建:除了從現有的數據源創建 Stream,我們還可以使用生成器來生成元素。Java 8 中提供了 Stream.generate() 方法和 Stream.iterate() 方法來創建無限 Stream。例如:

    Stream<Integer> stream = Stream.generate(() -> 0); // 創建一個無限流,每個元素都是 0
    Stream<Integer> stream = Stream.iterate(0, n -> n + 1); // 創建一個無限流,從 0 開始遞增
    

1.2 Stream中的方法

stream的操作分為兩大類,一類為中間操作,一類為終端操作。

中間操作:返回值仍然為一個流,不會消耗流

終端操作:返回最終結果;終端操作會消耗掉流,使之不再可用

1.3 Stream中的注意事項:

  1. 流調用非終結方法返回值都是Stream本身類型,但是返回的並不是自身的對象,返回的結果是一個新的流
  2. 流只能一次性使用,不能多次使用。

二 具體操作

01 遍歷操作(forEach和peek) 終

forEach

在Stream中有一個方法叫做forEach,可以對流中的元素進行逐一處理,逐一操作 void forEach(Consumer action):對流中的每一個元素進行逐一操作,逐一處理。參數Consumer表示處理規則。

Consumer是一個函數式接口,這個接口中只有一個抽象方法 void accept(T t):對數據進行操作,進行處理。

forEach方法的參數是Consumer函數式接口,那么可以傳遞Lambda表達式,這個Lambda表達式表示的是Consumer接口中唯一的一個抽象方法 accept的內容,我們要在Lambda表達式中編寫操作規則。]

Arrays.asList("Alice", "Bob", "Charlie").stream().forEach(System.out::println);

peek: peek是一個中間操作方法,它接受一個Consumer函數作為參數,對流中的每個元素執行該函數。與forEach不同的是,peek方法會返回一個新的流,該流中的元素和原始流中的元素相同

/**
     * peek(中間操作) 使用示例
     */
    @Test
    public void testPeek() {
        List<Student> list = Arrays.asList(new Student("張三", 18), new Student("李四", 19));
        //年齡脫敏 並做一些其他處理(略)
        List<Student> resultList = list.stream().peek(s -> s.setAge(null)).collect(Collectors.toList());
        System.out.println(resultList);
        //[StreamTest.Student(name=張三, age=null), StreamTest.Student(name=李四, age=null)]
   }

02 過濾操作 filter

在Stream中有一個方法叫做filter,可以對流中的元素進行過濾篩選。 Stream filter(Predicate predicate):用來對流中的元素進行過濾篩選,返回值是過濾后新的流。參數predicate表示過濾規則。

Predicate是一個函數式接口,里面只有一個抽象方法 boolean test(T t):判斷數據是否符合要求。

filter方法參數是Predicate函數式接口,所以可以傳遞Lambda表達式,該Lambda表達式表示Predicate接口中的唯一的抽象方法 test的內容。我們要在Lambda表達式中編寫驗證(判斷)規則。 如果我們希望某個數據留下,那么就返回true,如果不希望某個數據 留下,那么就返回false。

03 截斷 limit

在Stream中有一個方法叫做limit,可以獲取流中的前幾個元素。 Stream limit(long n):獲取流中的前n個元素然后放入到新的流中返回。

04 跳過 skip

在Stream中有一個方法叫做skip,可以跳過流中的前幾個元素,獲取剩下的元素 Stream skip(long n):跳過流中前n個元素,獲取剩下的元素放到一個新的流中返回。

05 去重(Distinct)

去重(Distinct):distinct() 方法用於去除 Stream 中的重復元素,根據元素的 equals()hashCode() 方法來判斷是否重復。例如:

java復制代碼Stream<Integer> stream = Stream.of(1, 2, 2, 3, 3, 3);
Stream<Integer> distinctStream = stream.distinct(); // 去重

06 合並 concat

在Stream中有一個靜態方法叫做concat,可以對兩個流進行合並,合並成新的流。 static Stream concat(Stream a, Stream b):對a和b這兩個流進行合並,合並成新的流返回。 在Stream中有一個方法就叫做map,可以將流中的元素【映射】到另一個流中。

映射其實指的就是將原來流中的每一個元素都進行某種操作,然后將操作后的元素保存到新的流中。

07 映射 map 終

Stream中的map方法: Stream map(Function mapper):將流中的元素映射到新的流中並返回。參數Function表示映射規則。

Function是一個函數式接口,里面只有一個抽象方法叫做apply R apply(T t):對數據進行處理,然后返回結果。

map方法的參數是Function這個函數式接口,那么我們可以傳遞Lambda表達式, 這個Lambda表達式表示的Function中唯一的一個抽象方法 map方法的內容,我們在Lambda表達式中編寫處理的規則。

示例代碼

 public static void main(String[] args) {
        List<Student> list = new ArrayList<Student>();
        list.add(new Student(1, "張三", 18, "北京"));
        list.add(new Student(2, "李四", 18, "北京"));
        list.add(new Student(3, "王五", 18, "南京"));
        list.add(new Student(4, "錢二", 18, "北京"));
        List<Integer> collect = list.stream().map(o -> o.getId()).collect(Collectors.toList());
        collect.forEach(System.out::print);
        //1234

08 扁平映射 flatMap

扁平映射(FlatMap):flatMap() 方法類似於 map() 方法,不同之處在於它可以將每個元素映射為一個流,並將所有流連接成一個流。這主要用於解決嵌套集合的情況

簡單來說,flatMap()將集合的集合降維成單個元素的集合

代碼示例

         List<Integer> list1 = Arrays.asList(1, 2, 3);
         List<Integer> list2 = Arrays.asList(4, 5, 6);
         List<Integer> list3 = Arrays.asList(7, 8, 3);
         List<List<Integer>> lists = Arrays.asList(list1, list2, list3);
         List<Integer> aggregationList = lists.stream().flatMap(o ->             o.stream()).collect(Collectors.toList());
         System.out.println(aggregationList);//[1, 2, 3, 4, 5, 6, 7, 8, 3]

09 匹配操作 (allMatch、anyMatch 和 noneMatch)

  • anyMatch() :判斷條件中,任意一個元素判斷為true,則返回true
  • allMatch() : 判斷條件中,全部元素判斷為true,則返回true
  • noneMatch(): 判斷條件中,全部元素判斷為flase,則返回true

stream.allMatch() 和 stream.anyMatch()均為終端操作

傳入一個Predicate函數式接口,用於指定條件

   @Test
   public void testMatch() {
       List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
       boolean allEven = numbers.stream()
               .allMatch(n -> n % 2 == 0);
       System.out.println(allEven); // 輸出結果: false
       boolean hasEven = numbers.stream()
               .anyMatch(n -> n % 2 == 0);
       System.out.println(hasEven); // 輸出結果: true
       boolean noneNegative = numbers.stream()
               .noneMatch(n -> n < 0);
       System.out.println(noneNegative); // 輸出結果: true
   }

10 統計操作 (count、max 和 min)終

在 Stream API 中,count、max 和 min 是用於統計操作的方法,它們可以用來獲取流中元素的數量、最大值和最小值。

  1. count: count 方法用於返回流中元素的數量。它返回一個 long 類型的值,表示流中的元素個數。
  2. max: max 方法用於返回流中的最大值。它返回一個 Optional 對象,如果流為空,則返回一個空的 Optional;如果流非空,則返回流中的最大值的 Optional。
  3. min: min 方法用於返回流中的最小值。它返回一個 Optional 對象,如果流為空,則返回一個空的 Optional;如果流非空,則返回流中的最小值的 Optional。

未傳入Comparator則填null,默認用Comparable的compareTo函數比較。

   @Test
   public void testMinMaxCount(){
       List<Integer> list = Arrays.asList(1, 15, 89, null, 2, 56);
       //求最大值,元素為null時會拋出異常
       Optional<Integer> maxOpt = list.stream().filter(Objects::nonNull).max(Integer::compareTo);
       maxOpt.ifPresent(System.out::println);//89
       //求最小值,元素為null時會拋出異常
       Optional<Integer> minOpt = list.stream().filter(Objects::nonNull).min(Integer::compareTo);
       minOpt.ifPresent(System.out::println);//1
       //求元素的個數,元素為null時不計入
       long count = list.stream().filter(Objects::nonNull).count();
       System.out.println("元素個數: "+count);//元素個數: 5     
   }

11 查找操作 (findFirst 和 findAny) 終

findFirst: findFirst 方法用於返回流中的第一個元素。它返回一個 Optional 對象,如果流為空,則返回一個空的 Optional;如果流非空,則返回流中的第一個元素的 Optional。

    @Test
    public void testFindFirst() {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        Optional<String> first = names.stream().findFirst();
        first.ifPresent(System.out::println); // 輸出結果: Alice
    }

findAny: findAny 方法用於返回流中的任意一個元素。它返回一個 Optional 對象,如果流為空,則返回一個空的 Optional;如果流非空,則返回流中的任意一個元素的 Optional。在順序流中,通常會返回第一個元素;而在並行流中,由於多線程的處理,可能返回不同的元素。

  @Test
    public void testFindAny() {
        // 在順序流中,通常會返回第一個元素;
        List<Integer> list = Arrays.asList(4, 5, 61, 2, 3);
        for (int i = 0; i < 10; i++) {
            Optional<Integer> any = list.stream().findAny();
            any.ifPresent(System.out::println);//4
        }
        System.out.println("-----------------------");
        // 而在並行流中,由於多線程的處理,可能返回不同的元素。
        for (int i = 0; i < 100; i++) {
            Optional<Integer> any = list.stream().parallel().findAny();
            any.ifPresent(System.out::println);//能返回不同的元素
        }
    }

12 排序 sorted

按照自然順序或者使用自定義的比較器進行排序。

排序操作的語法如下:

Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator)

第一種語法形式中,sorted() 方法會根據元素的自然順序進行排序。如果元素實現了 Comparable 接口並且具備自然順序,那么可以直接調用該方法進行排序。

第二種語法形式中,sorted(Comparator<? super T> comparator) 方法接受一個比較器(Comparator)作為參數,用於指定元素的排序規則。通過自定義比較器,可以對非 Comparable 類型的對象進行排序。

   @Test
    public void testSorted() {
        //1按自然順序排序
        List<Integer> nums = Arrays.asList(4, 5, 61, 2, 3);
        System.out.println(nums.stream().sorted().collect(Collectors.toList()));
        //2 通過自定義比較器排序
        List<Student> list = Arrays.asList(new Student("張三", 18), new Student("李四", 19));
        //stream流倒序排序
        list = list.stream().sorted(Comparator.comparing(Student::getAge).reversed()).collect(Collectors.toList());
        //stream流多條件排序
        list = list.stream().sorted(Comparator.comparing(Student::getAge).thenComparing((Student::getName))).collect(Collectors.toList());
          // s1-s2 升序   s2-s1降序

       /* //list升序排列
        Collections.sort(list, (s1, s2) -> s1.getAge().compareTo(s2.getAge()));
        list.forEach(stu -> System.out.println(stu));*/
    }

13 聚合操作 reduce 終

reduce: reduce是一個終端操作方法,它接受一個BinaryOperator函數作為參數,對流中的元素逐個進行合並操作,最終得到一個結果。該方法會將流中的第一個元素作為初始值,然后將初始值與下一個元素傳遞給BinaryOperator函數進行計算,得到的結果再與下一個元素進行計算,以此類推,直到遍歷完所有元素。

  @Test
    public void testReduce() {
        List<Student> list = Arrays.asList(new Student("張三", 18), new Student("李四", 19));
        //求年齡總和
        Integer sum = list.stream().map(Student::getAge).reduce(0, (Integer::sum));
        //求年齡的最大值,用reduce
        Integer max = list.stream().map(Student::getAge).reduce(Integer.MIN_VALUE, (result, element) -> result > element ? result : element);
        //reduce一個參數的重載形式內部的計算
        list.stream().map(Student::getAge).reduce((result, element) -> result > element ? result : element).ifPresent(System.out::println);
    }

14 收集流中的元素 stream.collect() 終

將流中的元素收集到集合中(將流轉成集合)

在Stream中有一個方法叫做collect,可以將流中的元素收集到集合(將流轉成集合) R collect(Collector collector):參數collector表示將數據收集到哪種集合。

Collector是一個接口,我們要使用這個接口的實現類對象,這個接口的實現類對象不是由我們去創建的,而是通過 工具類獲取,獲取Collector的工具類叫做Collectors

Collectors中獲取Collector的方法: static Collector toList():通過該方法獲取到的Collector對象表示將數據收集到List集合。 static Collector toSet():通過該方法獲取到的Collector對象表示將數據收集到Set集合。

收集到數組中

在Stream中有一個方法叫做toArray,可以將流中的內容收集到數組中(將流轉成數組) Object[] toArray():將流轉成數組

用法一:將流轉化為Collection或Map

Collectors.toCollection() 將數據轉換成Collection,只要是Collection的實現都可以,例如ArrayList,HashSet,該方法能夠接受一個Collection對象

示例:

轉化為list or set

 //List
 Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toCollection(ArrayList::new));
 //Set
 Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toCollection(HashSet::new));
 
 // Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toList());
 // Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toSet());

轉化為map

/**
 * @author : lyn
 * @date : 2022-05-08 17:09
 */
public class TestStream {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<Student>();
        list.add(new Student(1, "張三", 18, "北京"));
        list.add(new Student(2, "李四", 18, "北京"));
        list.add(new Student(3, "王五", 18, "南京"));
        list.add(new Student(4, "錢二", 18, "北京"));
        //1 將對象list集合轉變為,以id為key,自身為value的map集合
        Map<Integer, Student> map = list.stream().collect(Collectors.toMap(Student::getId, Function.identity()));
        for (Map.Entry<Integer, Student> entry : map.entrySet()) {
            System.out.println("key: " + entry.getKey() + ",value: " + entry.getValue());
        }
        //打印結果
        // key: 1,value: Student(id=1, name=張三, age=18, address=北京)
        //key: 2,value: Student(id=2, name=李四, age=18, address=北京)
        //key: 3,value: Student(id=3, name=王五, age=18, address=南京)
        //key: 4,value: Student(id=4, name=錢二, age=18, address=北京)

        //2 如果key重復會拋出java.lang.IllegalStateException: Duplicate key 1異常
        list.add(new Student(2, "錢二", 18, "北京"));
        // 解決方案:多傳入一個merge function來處理沖突
        Map<Integer, Student> map2 = list.stream().collect(Collectors.toMap(Student::getId, Function.identity(), (s, o) -> o));
        for (Map.Entry<Integer, Student> entry : map.entrySet()) {
            System.out.println("key: " + entry.getKey() + ",value: " + entry.getValue());
        }
    }
}

用法二:字符串聚合規約

Collectors.joining(),拼接,有三個重載方法,底層實現是StringBuilder,通過append方法拼接到一起,並且可以自定義分隔符(這個感覺還是很有用的,很多時候需要把一個list轉成一個String,指定分隔符就可以實現了,非常方便)、前綴、后綴。

  String str = Stream.of(1, 2, 3, 4, 5, 6).map(o -> o.toString()).collect(Collectors.joining(","));
         System.out.println(str);//1,2,3,4,5,6

用法三:統計個數

Collectors.counting() 統計元素個數,這個和Stream.count() 作用都是一樣的,返回的類型一個是包裝Long,另一個是基本long,但是他們的使用場景還是有區別的,這個后面再提。

 // Long 8
 Stream.of(1,0,-10,9,8,100,200,-80)
                 .collect(Collectors.counting());
 //如果僅僅只是為了統計,那就沒必要使用Collectors了,那樣更消耗資源
 // long 8
 Stream.of(1,0,-10,9,8,100,200,-80)
                 .count();

用法四:集合分組

Collectos.groupingBy()實現集合分組,返回值為一個Map

stream.forEach()遍歷流中的每一個元素,不一定依靠流的順序,而stream.forEachOrdered()按照流的順序遍歷。

         Student stu1 = new Student("李四",26,"北京");
         Student stu2 = new Student("李文",27,"北京");
         Student stu3 = new Student("趙四",27,"北京");
         Student stu4 = new Student("王五",28,"天津");
         Student stu5 = new Student("張三",26,"呼和浩特");
         Map<String, List<Student>> collect = Arrays.asList(stu1, stu2, stu3, stu4, stu5).
                 stream().collect(Collectors.groupingBy(Student::getAddress));
         collect.entrySet().stream().forEach((entry)->{
             System.out.println(entry.getKey()+"--------------------");
             entry.getValue().forEach(System.out::println);
 
         });

結果

呼和浩特-------------------- Student(name=張三, age=26, address=呼和浩特) 天津-------------------- Student(name=王五, age=28, address=天津) 北京-------------------- Student(name=李四, age=26, address=北京) Student(name=李文, age=27, address=北京) Student(name=趙四, age=27, address=北京)

三 並行流

3.1 什么是並行流

並行流是 Java 8 Stream API 中的一個特性。它可以將一個流的操作在多個線程上並行執行,以提高處理大量數據時的性能。

在傳統的順序流中,所有的操作都是在單個線程上按照順序執行的。而並行流則會將流的元素分成多個小塊,並在多個線程上並行處理這些小塊,最后將結果合並起來。這樣可以充分利用多核處理器的優勢,加快數據處理的速度。

要將一個順序流轉換為並行流,只需調用流的 parallel() 方法即可。示例代碼如下所示

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.stream()
                .parallel()
                .forEach(System.out::println);//順序不固定

在這個示例中,我們創建了一個包含整數的 List,並通過 stream() 方法將其轉換為流。接着調用 parallel() 方法將流轉換為並行流,然后使用 forEach 方法遍歷流中的元素並輸出。

需要注意的是,並行流的使用並不總是適合所有情況。並行流的優勢主要體現在數據量較大、處理時間較長的場景下。對於小規模數據和簡單的操作,順序流可能更加高效。在選擇使用並行流時,需要根據具體情況進行評估和測試,以確保獲得最佳的性能。

此外,還需要注意並行流在某些情況下可能引入線程安全的問題。如果多個線程同時訪問共享的可變狀態,可能會導致數據競爭和不確定的結果。因此,在處理並行流時,應當避免共享可變狀態,或采用適當的同步措施來確保線程安全。

3.2 如何使用並行流提高性能

使用並行流可以通過利用多線程並行處理數據,從而提高程序的執行性能。下面是一些使用並行流提高性能的常見方法:

  1. 創建並行流:要創建一個並行流,只需在普通流上調用 parallel() 方法。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    Stream<Integer> parallelStream = numbers.parallelStream();
    
  2. 利用任務並行性:並行流會將數據分成多個小塊,並在多個線程上並行處理這些小塊。這樣可以充分利用多核處理器的優勢。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    numbers.parallelStream()
           .map(n -> compute(n)) // 在多個線程上並行處理計算
           .forEach(System.out::println);
    

    在這個示例中,使用 map 方法對流中的每個元素進行計算。由於並行流的特性,計算操作會在多個線程上並行執行,提高了計算的效率。

  3. 避免共享可變狀態:在並行流中,多個線程會同時操作數據。如果共享可變狀態(如全局變量)可能導致數據競爭和不確定的結果。因此,避免在並行流中使用共享可變狀態,或者采取適當的同步措施來確保線程安全。

  4. 使用合適的操作:一些操作在並行流中的性能表現更好,而另一些操作則可能導致性能下降。一般來說,在並行流中使用基於聚合的操作(如 reducecollect)和無狀態轉換操作(如 mapfilter)的性能較好,而有狀態轉換操作(如 sorted)可能會導致性能下降。

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    
    // good performance
    int sum = numbers.parallelStream()
                     .reduce(0, Integer::sum);
    
    // good performance
    List<Integer> evenNumbers = numbers.parallelStream()
                                       .filter(n -> n % 2 == 0)
                                       .collect(Collectors.toList());
    
    // potential performance degradation
    List<Integer> sortedNumbers = numbers.parallelStream()
                                         .sorted()
                                         .collect(Collectors.toList());
    

    在這個示例中,reducefilter 的操作在並行流中具有良好的性能,而 sorted 操作可能導致性能下降。

除了上述方法,還應根據具體情況進行評估和測試,並行流是否能夠提高性能。有時候,並行流的開銷(如線程的創建和銷毀、數據切割和合並等)可能超過了其帶來的性能提升。因此,在選擇使用並行流時,應該根據數據量和操作復雜度等因素進行綜合考慮,以確保獲得最佳的性能提升。

3.3 並行流的適用場景和注意事項

  1. 大規模數據集:當需要處理大規模數據集時,使用並行流可以充分利用多核處理器的優勢,提高程序的執行效率。並行流將數據切分成多個小塊,並在多個線程上並行處理這些小塊,從而縮短了處理時間。
  2. 復雜的計算操作:對於復雜的計算操作,使用並行流可以加速計算過程。由於並行流能夠將計算操作分配到多個線程上並行執行,因此可以有效地利用多核處理器的計算能力,提高計算的速度。
  3. 無狀態轉換操作:並行流在執行無狀態轉換操作(如 mapfilter)時表現較好。這類操作不依賴於其他元素的狀態,每個元素的處理是相互獨立的,可以很容易地進行並行處理。

並行流的注意事項包括:

  1. 線程安全問題:並行流的操作是在多個線程上並行執行的,因此需要注意線程安全問題。如果多個線程同時訪問共享的可變狀態,可能會導致數據競爭和不確定的結果。在處理並行流時,應避免共享可變狀態,或者采用適當的同步措施來確保線程安全。
  2. 性能評估和測試:並行流的性能提升並不總是明顯的。在選擇使用並行流時,應根據具體情況進行評估和測試,以確保獲得最佳的性能提升。有時,並行流的開銷(如線程的創建和銷毀、數據切割和合並等)可能超過了其帶來的性能提升。
  3. 並發操作限制:某些操作在並行流中的性能表現可能較差,或者可能導致結果出現錯誤。例如,在並行流中使用有狀態轉換操作(如 sorted)可能導致性能下降或結果出現錯誤。在使用並行流時,應注意避免這類操作,或者在需要時采取適當的處理措施。
  4. 內存消耗:並行流需要將數據分成多個小塊進行並行處理,這可能導致額外的內存消耗。在處理大規模數據集時,應確保系統有足夠的內存來支持並行流的執行,以避免內存溢出等問題。

四 其他用例

4.1 Stream將Long類型的數組轉成int[]

Long[] longArray = {100L, 200L, 300L};
// 將Long類型的數組轉化為Stream對象,然后使用mapToInt方法將每個元素已經映射成對應的int值
int[] intArray = Arrays.stream(longArray).mapToInt(Long::intValue).toArray();

4.2 獲取1-5的集合

指定一個常量seed,生成從seed到常量f(由UnaryOperator返回的值得到)的流。

//獲取1-5的集合
List<Integer> list = Stream.iterate(1, n -> n+1 ).limit(5).collect(Collectors.toList());

4.3 將以逗號分割的數字字符串轉為集合

    /**
     * 將字符串 12,45,89 轉成List<Long>
     */
    @Test
    public void testStr2List() {
        String str = "fghj,48,drftyguhji,,";
        List<Long> ids = Stream
                .of(str.split(","))
                .filter(s -> Pattern.matches("^[1-9]\\d*|0$", s))
            	//.filter(NumberUtils::isDigits)//import org.apache.commons.lang3.math.NumberUtils;
                .map(Long::valueOf)
                .collect(Collectors.toList());
        System.out.println(ids);//[48]
    }

4.4 統計各類型的交易金額

  @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class  Item {
        private Integer id;
        private String type;
        private Long money;
    }

    @Test
    public void testCollectMap() {
        //統計各類型的交易金額
        String[] types = {"娛樂", "飲食", "交通"};
        List<Item> baseList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            baseList.add(new Item(i, types[i % 3], 10L));
        }
        baseList.add(new Item(18, types[1], null));
        System.out.println(baseList);
        Map<String, Long> statMap = baseList
                .stream()
                .filter(s -> Objects.nonNull(s.getMoney()))//注意空指針
                .collect(Collectors.toMap(Item::getType, Item::getMoney, Long::sum));
        System.out.println(statMap);//{飲食=30, 娛樂=40, 交通=30}
    }

4.5 lambda 表達式中設置序號

    @Test
    public void testLambdaSetSortNum() {
        //基礎數據准備
        List<NumStudent> list = Arrays.asList(new NumStudent(null, "張三", 18), new NumStudent(null, "李四", 19), new NumStudent(null, "李無", 20));
        //需求根據年齡大小排序並設置序號
        //1 利用數組
        //int[] index={1};
        //list.stream().sorted(Comparator.comparing(NumStudent::getAge)).forEach(s -> s.setNum(index[0]++));
        //2 利用AtomicInteger
        AtomicInteger index = new AtomicInteger();
        list.stream().sorted(Comparator.comparing(NumStudent::getAge)).forEach(s -> s.setNum(index.incrementAndGet()));
        System.out.println(list);
        //[StreamTest.NumStudent(num=1, name=張三, age=18), StreamTest.NumStudent(num=2, name=李四, age=19), StreamTest.NumStudent(num=3, name=李無, age=20)]
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM