- 一 Stream簡述
- 二 具體操作
- 三 並行流
- 四 其他用例
參考資料地址1:
java Steam的使用
參考資料地址2:
java Steam基礎練習
參考資料地址3:
Java8的Steam流常用方法和總結
參考資料地址4:
萬字詳解 Stream 流式編程,寫代碼也可以很優雅
參考資料地址5:
stream流分組
一 Stream簡述
Stream
是一個接口,該接口表示流。
1.1 獲取流的幾種方式
通過Collection集合(單列集合)調用stream()方法獲取。【根據單列集合獲取】
Collection中獲取流的方法: Stream
stream():獲取集合對應的流。獲取流 通過Stream中的靜態方法根據數組獲取流。【根據數組獲取】
在Stream中有一個靜態方法,可以根據數組獲取流。
Stream中根據數組獲取流的方法: static
Stream of(T... values):根據數組或多個元素獲取Stream流。 Arrays.stream(T array)
通過 Stream.builder() 創建: Stream
通過生成器創建:除了從現有的數據源創建 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中的注意事項:
- 流調用非終結方法返回值都是Stream本身類型,但是返回的並不是自身的對象,返回的結果是一個新的流
- 流只能一次性使用,不能多次使用。
二 具體操作
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 是用於統計操作的方法,它們可以用來獲取流中元素的數量、最大值和最小值。
- count: count 方法用於返回流中元素的數量。它返回一個 long 類型的值,表示流中的元素個數。
- max: max 方法用於返回流中的最大值。它返回一個 Optional 對象,如果流為空,則返回一個空的 Optional;如果流非空,則返回流中的最大值的 Optional。
- 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 如何使用並行流提高性能
使用並行流可以通過利用多線程並行處理數據,從而提高程序的執行性能。下面是一些使用並行流提高性能的常見方法:
-
創建並行流:要創建一個並行流,只需在普通流上調用
parallel()
方法。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> parallelStream = numbers.parallelStream();
-
利用任務並行性:並行流會將數據分成多個小塊,並在多個線程上並行處理這些小塊。這樣可以充分利用多核處理器的優勢。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); numbers.parallelStream() .map(n -> compute(n)) // 在多個線程上並行處理計算 .forEach(System.out::println);
在這個示例中,使用
map
方法對流中的每個元素進行計算。由於並行流的特性,計算操作會在多個線程上並行執行,提高了計算的效率。 -
避免共享可變狀態:在並行流中,多個線程會同時操作數據。如果共享可變狀態(如全局變量)可能導致數據競爭和不確定的結果。因此,避免在並行流中使用共享可變狀態,或者采取適當的同步措施來確保線程安全。
-
使用合適的操作:一些操作在並行流中的性能表現更好,而另一些操作則可能導致性能下降。一般來說,在並行流中使用基於聚合的操作(如
reduce
、collect
)和無狀態轉換操作(如map
、filter
)的性能較好,而有狀態轉換操作(如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());
在這個示例中,
reduce
和filter
的操作在並行流中具有良好的性能,而sorted
操作可能導致性能下降。
除了上述方法,還應根據具體情況進行評估和測試,並行流是否能夠提高性能。有時候,並行流的開銷(如線程的創建和銷毀、數據切割和合並等)可能超過了其帶來的性能提升。因此,在選擇使用並行流時,應該根據數據量和操作復雜度等因素進行綜合考慮,以確保獲得最佳的性能提升。
3.3 並行流的適用場景和注意事項
- 大規模數據集:當需要處理大規模數據集時,使用並行流可以充分利用多核處理器的優勢,提高程序的執行效率。並行流將數據切分成多個小塊,並在多個線程上並行處理這些小塊,從而縮短了處理時間。
- 復雜的計算操作:對於復雜的計算操作,使用並行流可以加速計算過程。由於並行流能夠將計算操作分配到多個線程上並行執行,因此可以有效地利用多核處理器的計算能力,提高計算的速度。
- 無狀態轉換操作:並行流在執行無狀態轉換操作(如
map
、filter
)時表現較好。這類操作不依賴於其他元素的狀態,每個元素的處理是相互獨立的,可以很容易地進行並行處理。
並行流的注意事項包括:
- 線程安全問題:並行流的操作是在多個線程上並行執行的,因此需要注意線程安全問題。如果多個線程同時訪問共享的可變狀態,可能會導致數據競爭和不確定的結果。在處理並行流時,應避免共享可變狀態,或者采用適當的同步措施來確保線程安全。
- 性能評估和測試:並行流的性能提升並不總是明顯的。在選擇使用並行流時,應根據具體情況進行評估和測試,以確保獲得最佳的性能提升。有時,並行流的開銷(如線程的創建和銷毀、數據切割和合並等)可能超過了其帶來的性能提升。
- 並發操作限制:某些操作在並行流中的性能表現可能較差,或者可能導致結果出現錯誤。例如,在並行流中使用有狀態轉換操作(如
sorted
)可能導致性能下降或結果出現錯誤。在使用並行流時,應注意避免這類操作,或者在需要時采取適當的處理措施。 - 內存消耗:並行流需要將數據分成多個小塊進行並行處理,這可能導致額外的內存消耗。在處理大規模數據集時,應確保系統有足夠的內存來支持並行流的執行,以避免內存溢出等問題。
四 其他用例
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)]
}