Java8使用Stream流實現(xiàn)List列表查詢、統(tǒng)計、排序以及分組
Java8提供了Stream(流)處理集合的關鍵抽象概念,它可以對集合進行操作,可以執(zhí)行非常復雜的查找、過濾和映射數(shù)據(jù)等操作。Stream API 借助于同樣新出現(xiàn)的Lambda表達式,極大的提高編程效率和程序可讀性。
下面是使用Stream的常用方法的綜合實例。
創(chuàng)建User類作為持久層。
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.math.BigDecimal; @Data @AllArgsConstructor @NoArgsConstructor public class User { int id; String name; String sex; int age; String Department; BigDecimal Salary; }
創(chuàng)建UserService.class(用戶信息業(yè)務邏輯類)。
package com.wsq; import com.wsq.pojo.User; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /* * 用戶信息業(yè)務邏輯類 * @author wsq */ public class UserService { /* * 獲取用戶列表 */ public static List<User> getUserList() { List<User> userList = new ArrayList<User>(); userList.add(new User(1, "wsq的博客_01", "男", 32, "研發(fā)部", BigDecimal.valueOf(1600))); userList.add(new User(2, "wsq的博客_02", "男", 30, "財務部", BigDecimal.valueOf(1800))); userList.add(new User(3, "wsq的博客_03", "女", 20, "人事部", BigDecimal.valueOf(1700))); userList.add(new User(4, "wsq的博客_04", "男", 38, "研發(fā)部", BigDecimal.valueOf(1500))); userList.add(new User(5, "wsq的博客_05", "女", 25, "財務部", BigDecimal.valueOf(1200))); return userList; } }
一、查詢方法
1.1 forEach()
使用 forEach() 遍歷列表數(shù)據(jù)。
/* * 使用forEach()遍歷列表信息 * @author wsq */ @Test public void forEachTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //遍歷用戶列表 System.out.println("---------------輸出方法一-----------------"); userList.forEach(user -> {System.out.println(user);}); System.out.println("---------------輸出方法二-----------------"); userList.forEach(System.out::println); }
控制臺輸出:
1.2 filter(T -> boolean)
使用 filter() 過濾列表數(shù)據(jù)。
【示例】獲取部門為“研發(fā)部”的用戶列表。
/* * 使用filter()過濾列表信息 * @author wsq */ @Test public void filterTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //獲取部門為“研發(fā)部”的用戶列表 userList = userList.stream().filter(user -> user.getDepartment() == "研發(fā)部").collect(Collectors.toList()); //遍歷用戶列表 userList.forEach(System.out::println); }
控制臺輸出:
1.3 findAny() 和 findFirst()
使用 findAny() 和 findFirst() 獲取第一條數(shù)據(jù)。
【示例】獲取用戶名稱為“wsq的博客_02”的用戶信息,如果未找到則返回null。
/* * 使用findAny()獲取第一條數(shù)據(jù) * @author wsq */ @Test public void findAnytTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //獲取用戶名稱為“pan_junbiao的博客_02”的用戶信息,如果沒有找到則返回null User user = userList.stream().filter(u -> u.getName().equals("wsq的博客_02")).findAny().orElse(null); //打印用戶信息 System.out.println(user); }
控制臺輸出:
注意:findFirst() 和 findAny() 都是獲取列表中的第一條數(shù)據(jù),但是findAny()操作,返回的元素是不確定的,對于同一個列表多次調(diào)用findAny()有可能會返回不同的值。使用findAny()是為了更高效的性能。如果是數(shù)據(jù)較少,串行地情況下,一般會返回第一個結果,如果是并行(parallelStream并行流)的情況,那就不能確保是第一個。
例如:使用parallelStream并行流,findAny() 返回的就不一定是第一條數(shù)據(jù)。
//parallelStream方法能生成并行流,使用findAny返回的不一定是第一條數(shù)據(jù) User user = userList.parallelStream().filter(u -> u.getName().startsWith("wsq")).findAny().orElse(null);
1.4 map(T -> R) 和 flatMap(T -> Stream)
使用 map() 將流中的每一個元素 T 映射為 R(類似類型轉(zhuǎn)換)。
使用 flatMap() 將流中的每一個元素 T 映射為一個流,再把每一個流連接成為一個流。
【示例】使用 map() 方法獲取用戶列表中的名稱列。
/* * 使用map()獲取列元素 * @author wsq */ @Test public void mapTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //獲取用戶名稱列表 List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList()); //或者:List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList()); //遍歷名稱列表 nameList.forEach(System.out::println); }
控制臺輸出:
【示例】使用 flatMap() 將流中的每一個元素連接成為一個流。
/* * 使用flatMap()將流中的每一個元素連接成為一個流 * @author wsq */ @Test public void flatMapTest(){ //創(chuàng)建城市 List<String> cityList = new ArrayList<String>(); cityList.add("北京;上海;深圳;"); cityList.add("廣州;武漢;杭州;"); //分隔城市列表,使用 flatMap() 將流中的每一個元素連接成為一個流。 cityList = cityList.stream() .map(city -> city.split(";")) .flatMap(Arrays::stream) .collect(Collectors.toList()); //遍歷城市列表 cityList.forEach(System.out::println); }
控制臺輸出:
1.5 distinct()
使用 distinct() 方法可以去除重復的數(shù)據(jù)。
【示例】獲取部門列表,并去除重復數(shù)據(jù)。
/** * 使用distinct()去除重復數(shù)據(jù) * @author wsq */ @Test public void distinctTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //獲取部門列表,并去除重復數(shù)據(jù) List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList()); //遍歷部門列表 departmentList.forEach(System.out::println); }
控制臺輸出:
1.6 limit(long n) 和 skip(long n)
limit(long n) 方法用于返回前n條數(shù)據(jù),skip(long n) 方法用于跳過前n條數(shù)據(jù)。
【示例】獲取用戶列表,要求跳過第1條數(shù)據(jù)后的前3條數(shù)據(jù)。
/* * limit(long n)方法用于返回前n條數(shù)據(jù) * skip(long n)方法用于跳過前n條數(shù)據(jù) * @author wsq */ @Test public void limitAndSkipTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //獲取用戶列表,要求跳過第1條數(shù)據(jù)后的前3條數(shù)據(jù) userList = userList.stream() .skip(1) .limit(3) .collect(Collectors.toList()); //遍歷用戶列表 userList.forEach(System.out::println); }
控制臺輸出:
二、判斷方法
2.1 anyMatch(T -> boolean)
使用 anyMatch(T -> boolean) 判斷流中是否有一個元素匹配給定的 T -> boolean 條件。
2.2 allMatch(T -> boolean)
使用 allMatch(T -> boolean) 判斷流中是否所有元素都匹配給定的 T -> boolean 條件。
2.3 noneMatch(T -> boolean)
使用 noneMatch(T -> boolean) 流中是否沒有元素匹配給定的 T -> boolean 條件。
【示例】使用 anyMatch()、allMatch()、noneMatch() 進行判斷。
/* * 使用 anyMatch()、allMatch()、noneMatch() 進行判斷 * @author wsq */ @Test public void matchTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //判斷用戶列表中是否存在名稱為“pan_junbiao的博客_01”的數(shù)據(jù) boolean result1 = userList.stream().anyMatch(user -> user.getName().equals("pan_junbiao的博客_01")); //判斷用戶名稱是否都包含“pan_junbiao的博客”字段 boolean result2 = userList.stream().allMatch(user -> user.getName().contains("pan_junbiao的博客")); //判斷用戶名稱是否存在不包含“pan_junbiao的博客”字段 boolean result3 = userList.stream().noneMatch(user -> user.getName().contains("pan_junbiao的博客")); //打印結果 System.out.println(result1); System.out.println(result2); System.out.println(result3); }
控制臺輸出:
三、統(tǒng)計方法
3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
使用 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T) 用于組合流中的元素,如求和,求積,求最大值等。
【示例】使用 reduce() 求用戶列表中年齡的最大值、最小值、總和。
/* * 使用 reduce() 方法 * @author wsq */ @Test public void reduceTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //用戶列表中年齡的最大值、最小值、總和 int maxVal = userList.stream().map(User::getAge).reduce(Integer::max).get(); int minVal = userList.stream().map(User::getAge).reduce(Integer::min).get(); int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum); //打印結果 System.out.println("最大年齡:" + maxVal); System.out.println("最小年齡:" + minVal); System.out.println("年齡總和:" + sumVal); }
控制臺輸出:
3.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long)
int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);計算元素總和的方法其中暗含了裝箱成本,map(User::getAge) 方法過后流變成了 Stream 類型,而每個 Integer 都要拆箱成一個原始類型再進行 sum 方法求和,這樣大大影響了效率。針對這個問題 Java 8 有良心地引入了數(shù)值流 IntStream, DoubleStream, LongStream,這種流中的元素都是原始數(shù)據(jù)類型,分別是 int,double,long。
流轉(zhuǎn)換為數(shù)值流:
- mapToInt(T -> int) : return IntStream
- mapToDouble(T -> double) : return DoubleStream
- mapToLong(T -> long) : return LongStream
【示例】使用 mapToInt() 求用戶列表中年齡的最大值、最小值、總和、平均值。
/* * 使用 mapToInt() 方法 * @author wsq */ @Test public void mapToIntTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //用戶列表中年齡的最大值、最小值、總和、平均值 int maxVal = userList.stream().mapToInt(User::getAge).max().getAsInt(); int minVal = userList.stream().mapToInt(User::getAge).min().getAsInt(); int sumVal = userList.stream().mapToInt(User::getAge).sum(); double aveVal = userList.stream().mapToInt(User::getAge).average().getAsDouble(); //打印結果 System.out.println("最大年齡:" + maxVal); System.out.println("最小年齡:" + minVal); System.out.println("年齡總和:" + sumVal); System.out.println("平均年齡:" + aveVal); }
控制臺輸出:
3.3 counting() 和 count()
使用 counting() 和 count() 可以對列表數(shù)據(jù)進行統(tǒng)計。
【示例】使用 count() 統(tǒng)計用戶列表信息。
/* * 使用 counting() 或 count() 統(tǒng)計 * @author wsq */ @Test public void countTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //統(tǒng)計研發(fā)部的人數(shù),使用 counting()方法進行統(tǒng)計 Long departCount = userList.stream().filter(user -> user.getDepartment() == "研發(fā)部").collect(Collectors.counting()); //統(tǒng)計30歲以上的人數(shù),使用 count()方法進行統(tǒng)計(推薦) Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count(); //統(tǒng)計薪資大于1500元的人數(shù) Long salaryCount = userList.stream().filter(user -> user.getSalary().compareTo(BigDecimal.valueOf(1500)) == 1).count(); //打印結果 System.out.println("研發(fā)部的人數(shù):" + departCount + "人"); System.out.println("30歲以上的人數(shù):" + ageCount + "人"); System.out.println("薪資大于1500元的人數(shù):" + salaryCount + "人"); }
控制臺輸出:
3.4 summingInt()、summingLong()、summingDouble()
用于計算總和,需要一個函數(shù)參數(shù)。
/* * 使用 summingInt()、summingLong()、summingDouble() * @author wsq */ @Test public void sumTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //計算年齡總和 int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge)); //打印結果 System.out.println("年齡總和:" + sumAge); }
控制臺輸出:
3.5 averagingInt()、averagingLong()、averagingDouble()
用于計算平均值。
/* * 使用 summingInt()、summingLong()、summingDouble() * @author wsq */ @Test public void sumTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //計算平均年齡 double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge)); //打印結果 System.out.println("平均年齡:" + aveAge); }
控制臺輸出:
3.6 summarizingInt()、summarizingLong()、summarizingDouble()
這三個方法比較特殊,比如 summarizingInt 會返回 IntSummaryStatistics 類型。
IntSummaryStatistics類提供了用于計算的平均值、總數(shù)、最大值、最小值、總和等方法,方法如下圖:
【示例】使用 IntSummaryStatistics 統(tǒng)計:最大值、最小值、總和、平均值、總數(shù)。
/* * 使用 summarizingInt 統(tǒng)計 * @author wsq */ @Test public void summarizingIntTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //獲取IntSummaryStatistics對象 IntSummaryStatistics ageStatistics = userList.stream().collect(Collectors.summarizingInt(User::getAge)); //統(tǒng)計:最大值、最小值、總和、平均值、總數(shù) System.out.println("最大年齡:" + ageStatistics.getMax()); System.out.println("最小年齡:" + ageStatistics.getMin()); System.out.println("年齡總和:" + ageStatistics.getSum()); System.out.println("平均年齡:" + ageStatistics.getAverage()); System.out.println("員工總數(shù):" + ageStatistics.getCount()); }
控制臺輸出:
3.7 BigDecimal類型的統(tǒng)計
對于資金相關的字段,通常會使用BigDecimal數(shù)據(jù)類型。
【示例】統(tǒng)計用戶薪資信息。
/* * BigDecimal類型的統(tǒng)計 * @author wsq */ @Test public void BigDecimalTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //最高薪資 BigDecimal maxSalary = userList.stream().map(User::getSalary).max((x1, x2) -> x1.compareTo(x2)).get(); //最低薪資 BigDecimal minSalary = userList.stream().map(User::getSalary).min((x1, x2) -> x1.compareTo(x2)).get(); //薪資總和 BigDecimal sumSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add); //平均薪資 BigDecimal avgSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(userList.size()), 2, BigDecimal.ROUND_HALF_UP); //打印統(tǒng)計結果 System.out.println("最高薪資:" + maxSalary + "元"); System.out.println("最低薪資:" + minSalary + "元"); System.out.println("薪資總和:" + sumSalary + "元"); System.out.println("平均薪資:" + avgSalary + "元"); }
控制臺輸出:
四、排序方法
4.1 sorted() / sorted((T, T) -> int)
如果流中的元素的類實現(xiàn)了 Comparable 接口,即有自己的排序規(guī)則,那么可以直接調(diào)用 sorted() 方法對元素進行排序,如 Stream。反之, 需要調(diào)用 sorted((T, T) -> int) 實現(xiàn) Comparator 接口。
【示例】根據(jù)用戶年齡進行排序。
/* * 使用 sorted() 排序 * @author wsq */ @Test public void sortedTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //根據(jù)年齡排序(升序) userList = userList.stream().sorted((u1, u2) -> u1.getAge() - u2.getAge()).collect(Collectors.toList()); //推薦:userList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList()); //降序:userList = userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).collect(Collectors.toList()); //遍歷用戶列表 userList.forEach(System.out::println); }
控制臺輸出:
五、分組方法
5.1 groupingBy
使用 groupingBy() 將數(shù)據(jù)進行分組,最終返回一個 Map 類型。
【示例】根據(jù)部門對用戶列表進行分組。
/* * 使用 groupingBy() 分組 * @author wsq */ @Test public void groupingByTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //根據(jù)部門對用戶列表進行分組 Map<String,List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment)); //遍歷分組后的結果 userMap.forEach((key, value) -> { System.out.println(key + ":"); value.forEach(System.out::println); System.out.println("---------------------------------------------"); }); }
控制臺輸出:
5.2 多級分組
groupingBy 可以接受一個第二參數(shù)實現(xiàn)多級分組。
【示例】根據(jù)部門和性別對用戶列表進行分組。
/* * 使用 groupingBy() 多級分組 * @author wsq */ @Test public void multGroupingByTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //根據(jù)部門和性別對用戶列表進行分組 Map<String,Map<String,List<User>>> userMap = userList.stream() .collect(Collectors.groupingBy(User::getDepartment,Collectors.groupingBy(User::getSex))); //遍歷分組后的結果 userMap.forEach((key1, map) -> { System.out.println(key1 + ":"); map.forEach((key2,user)-> { System.out.println(key2 + ":"); user.forEach(System.out::println); }); System.out.println("-------------------------------------------------------"); }); }
5.3 分組匯總
【示例】根據(jù)部門進行分組,匯總各個部門用戶的平均年齡。
/* * 使用 groupingBy() 分組匯總 * @author wsq */ @Test public void groupCollectTest(){ //獲取用戶列表 List<User> userList = UserService.getUserList(); //根據(jù)部門進行分組,匯總各個部門用戶的平均年齡 Map<String, Double> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment, Collectors.averagingInt(User::getAge))); //遍歷分組后的結果 userMap.forEach((key, value) -> { System.out.println(key + "的平均年齡:" + value); }); }
總結
到此這篇關于Java8使用Stream流實現(xiàn)List列表查詢、統(tǒng)計、排序以及分組的文章就介紹到這了,更多相關Java8 Stream流常用方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
淺談java二進制、十進制、十六進制、字符串之間的相互轉(zhuǎn)換
下面小編就為大家?guī)硪黄獪\談二進制、十進制、十六進制、字符串之間的相互轉(zhuǎn)換。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考,一起跟隨小編過來看看吧2016-06-06徹底搞懂java并發(fā)ThreadPoolExecutor使用
這篇文章主要為大家介紹了徹底搞懂java并發(fā)ThreadPoolExecutor使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Java使用HttpUtils實現(xiàn)發(fā)送HTTP請求
這篇文章主要介紹了Java使用HttpUtils實現(xiàn)發(fā)送HTTP請求,HTTP請求,在日常開發(fā)中,還是比較常見的,今天給大家分享HttpUtils如何使用,需要的朋友可以參考下2023-05-05分別在Groovy和Java中創(chuàng)建并初始化映射的不同分析
這篇文章主要為大家介紹了分別在Groovy和Java中創(chuàng)建并初始化映射的不同分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-03-03