https://blog.csdn.net/pan_junbiao/article/details/105913518
Java8提供了Stream(流)處理集合的關鍵抽象概念,它可以對集合進行的操作,可以執行非常復雜的查找、過濾和映射數據等操作。Stream API 借助於同樣新出現的Lambda表達式,極大的提高編程效率和程序可讀性。
下面是使用Stream的常用方法的綜合實例。
創建UserService.class(用戶信息業務邏輯類)。
-
import com.pjb.streamdemo.entity.User;
-
import java.math.BigDecimal;
-
import java.util.ArrayList;
-
import java.util.List;
-
-
/**
-
* 用戶信息業務邏輯類
-
* @author pan_junbiao
-
**/
-
public class UserService
-
{
-
/**
-
* 獲取用戶列表
-
*/
-
public static List<User> getUserList()
-
{
-
List<User> userList = new ArrayList<User>();
-
userList.add( new User(1, "pan_junbiao的博客_01", "男", 32, "研發部", BigDecimal.valueOf(1600)));
-
userList.add( new User(2, "pan_junbiao的博客_02", "男", 30, "財務部", BigDecimal.valueOf(1800)));
-
userList.add( new User(3, "pan_junbiao的博客_03", "女", 20, "人事部", BigDecimal.valueOf(1700)));
-
userList.add( new User(4, "pan_junbiao的博客_04", "男", 38, "研發部", BigDecimal.valueOf(1500)));
-
userList.add( new User(5, "pan_junbiao的博客_05", "女", 25, "財務部", BigDecimal.valueOf(1200)));
-
return userList;
-
}
-
}
1、查詢方法
1.1 forEach()
使用 forEach() 遍歷列表數據。
-
/**
-
* 使用forEach()遍歷列表信息
-
* @author pan_junbiao
-
*/
-
-
public void forEachTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//遍歷用戶列表
-
userList.forEach(System.out::println);
-
}
上述遍歷語句等同於以下語句:
userList.forEach(user -> {System.out.println(user);});
執行結果:
1.2 filter(T -> boolean)
使用 filter() 過濾列表數據。
【示例】獲取部門為“研發部”的用戶列表。
-
/**
-
* 使用filter()過濾列表信息
-
* @author pan_junbiao
-
*/
-
-
public void filterTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//獲取部門為“研發部”的用戶列表
-
userList = userList.stream().filter(user -> user.getDepartment() == "研發部").collect(Collectors.toList());
-
-
//遍歷用戶列表
-
userList.forEach(System.out::println);
-
}
執行結果:
1.3 findAny() 和 findFirst()
使用 findAny() 和 findFirst() 獲取第一條數據。
【示例】獲取用戶名稱為“pan_junbiao的博客_02”的用戶信息,如果未找到則返回null。
-
/**
-
* 使用findAny()獲取第一條數據
-
* @author pan_junbiao
-
*/
-
-
public void findAnytTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//獲取用戶名稱為“pan_junbiao的博客_02”的用戶信息,如果沒有找到則返回null
-
User user = userList.stream().filter(u -> u.getName().equals( "pan_junbiao的博客_02")).findAny().orElse(null);
-
-
//打印用戶信息
-
System.out.println(user);
-
}
執行結果:
注意:findFirst() 和 findAny() 都是獲取列表中的第一條數據,但是findAny()操作,返回的元素是不確定的,對於同一個列表多次調用findAny()有可能會返回不同的值。使用findAny()是為了更高效的性能。如果是數據較少,串行地情況下,一般會返回第一個結果,如果是並行(parallelStream並行流)的情況,那就不能確保是第一個。
例如:使用parallelStream並行流,findAny() 返回的就不一定是第一條數據。
-
//parallelStream方法能生成並行流,使用findAny返回的不一定是第一條數據
-
User user = userList.parallelStream().filter(u -> u.getName().startsWith( "p")).findAny().orElse(null);
1.4 map(T -> R) 和 flatMap(T -> Stream)
使用 map() 將流中的每一個元素 T 映射為 R(類似類型轉換)。
使用 flatMap() 將流中的每一個元素 T 映射為一個流,再把每一個流連接成為一個流。
【示例】使用 map() 方法獲取用戶列表中的名稱列。
-
/**
-
* 使用map()獲取列元素
-
* @author pan_junbiao
-
*/
-
-
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);
-
}
返回的結果為數組類型,寫法如下:
-
//數組類型
-
String[] nameArray = userList.stream().map(User::getName).collect(Collectors.toList()).toArray( new String[userList.size()]);
執行結果:
【示例】使用 flatMap() 將流中的每一個元素連接成為一個流。
-
/**
-
* 使用flatMap()將流中的每一個元素連接成為一個流
-
* @author pan_junbiao
-
*/
-
-
public void flatMapTest()
-
{
-
//創建城市
-
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() 方法可以去除重復的數據。
【示例】獲取部門列表,並去除重復數據。
-
/**
-
* 使用distinct()去除重復數據
-
* @author pan_junbiao
-
*/
-
-
public void distinctTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//獲取部門列表,並去除重復數據
-
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條數據,skip(long n) 方法用於跳過前n條數據。
【示例】獲取用戶列表,要求跳過第1條數據后的前3條數據。
-
/**
-
* limit(long n)方法用於返回前n條數據
-
* skip(long n)方法用於跳過前n條數據
-
* @author pan_junbiao
-
*/
-
-
public void limitAndSkipTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//獲取用戶列表,要求跳過第1條數據后的前3條數據
-
userList = userList.stream()
-
.skip( 1)
-
.limit( 3)
-
.collect(Collectors.toList());
-
-
//遍歷用戶列表
-
userList.forEach(System.out::println);
-
}
執行結果:
2、判斷方法
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 pan_junbiao
-
*/
-
-
public void matchTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//判斷用戶列表中是否存在名稱為“pan_junbiao的博客_01”的數據
-
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);
-
}
執行結果:
3、統計方法
3.1 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)
使用 reduce((T, T) -> T) 和 reduce(T, (T, T) -> T) 用於組合流中的元素,如求和,求積,求最大值等。
【示例】使用 reduce() 求用戶列表中年齡的最大值、最小值、總和。
-
/**
-
* 使用 reduce() 方法
-
* @author pan_junbiao
-
*/
-
-
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 有良心地引入了數值流 IntStream, DoubleStream, LongStream,這種流中的元素都是原始數據類型,分別是 int,double,long。
流轉換為數值流:
- mapToInt(T -> int) : return IntStream
- mapToDouble(T -> double) : return DoubleStream
- mapToLong(T -> long) : return LongStream
【示例】使用 mapToInt() 求用戶列表中年齡的最大值、最小值、總和、平均值。
-
/**
-
* 使用 mapToInt() 方法
-
* @author pan_junbiao
-
*/
-
-
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() 可以對列表數據進行統計。
【示例】使用 count() 統計用戶列表信息。
-
/**
-
* 使用 counting() 或 count() 統計
-
* @author pan_junbiao
-
*/
-
-
public void countTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//統計研發部的人數,使用 counting()方法進行統計
-
Long departCount = userList.stream().filter(user -> user.getDepartment() == "研發部").collect(Collectors.counting());
-
-
//統計30歲以上的人數,使用 count()方法進行統計(推薦)
-
Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();
-
-
//統計薪資大於1500元的人數
-
Long salaryCount = userList.stream().filter(user -> user.getSalary().compareTo(BigDecimal.valueOf( 1500)) == 1).count();
-
-
//打印結果
-
System.out.println( "研發部的人數:" + departCount + "人");
-
System.out.println( "30歲以上的人數:" + ageCount + "人");
-
System.out.println( "薪資大於1500元的人數:" + salaryCount + "人");
-
}
執行結果:
3.4 summingInt()、summingLong()、summingDouble()
用於計算總和,需要一個函數參數。
-
//計算年齡總和
-
int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));
3.5 averagingInt()、averagingLong()、averagingDouble()
用於計算平均值。
-
//計算平均年齡
-
double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));
3.6 summarizingInt()、summarizingLong()、summarizingDouble()
這三個方法比較特殊,比如 summarizingInt 會返回 IntSummaryStatistics 類型。
IntSummaryStatistics類提供了用於計算的平均值、總數、最大值、最小值、總和等方法,方法如下圖:
【示例】使用 IntSummaryStatistics 統計:最大值、最小值、總和、平均值、總數。
-
/**
-
* 使用 summarizingInt 統計
-
* @author pan_junbiao
-
*/
-
-
public void summarizingIntTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//獲取IntSummaryStatistics對象
-
IntSummaryStatistics ageStatistics = userList.stream().collect(Collectors.summarizingInt(User::getAge));
-
-
//統計:最大值、最小值、總和、平均值、總數
-
System.out.println( "最大年齡:" + ageStatistics.getMax());
-
System.out.println( "最小年齡:" + ageStatistics.getMin());
-
System.out.println( "年齡總和:" + ageStatistics.getSum());
-
System.out.println( "平均年齡:" + ageStatistics.getAverage());
-
System.out.println( "員工總數:" + ageStatistics.getCount());
-
}
執行結果:
3.7 BigDecimal類型的統計
對於資金相關的字段,通常會使用BigDecimal數據類型。
【示例】統計用戶薪資信息。
-
/**
-
* BigDecimal類型的統計
-
* @author pan_junbiao
-
*/
-
-
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);
-
-
//打印統計結果
-
System.out.println( "最高薪資:" + maxSalary + "元");
-
System.out.println( "最低薪資:" + minSalary + "元");
-
System.out.println( "薪資總和:" + sumSalary + "元");
-
System.out.println( "平均薪資:" + avgSalary + "元");
-
}
執行結果:
4、排序方法
4.1 sorted() / sorted((T, T) -> int)
如果流中的元素的類實現了 Comparable 接口,即有自己的排序規則,那么可以直接調用 sorted() 方法對元素進行排序,如 Stream。反之, 需要調用 sorted((T, T) -> int) 實現 Comparator 接口。
【示例】根據用戶年齡進行排序。
-
/**
-
* 使用 sorted() 排序
-
* @author pan_junbiao
-
*/
-
-
public void sortedTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//根據年齡排序(升序)
-
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);
-
}
推薦使用如下寫法:
-
//升序
-
userList = userList.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());
-
-
//降序
-
userList = userList.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).collect(Collectors.toList());
執行結果:
5、分組方法
5.1 groupingBy
使用 groupingBy() 將數據進行分組,最終返回一個 Map 類型。
【示例】根據部門對用戶列表進行分組。
-
/**
-
* 使用 groupingBy() 分組
-
* @author pan_junbiao
-
*/
-
-
public void groupingByTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//根據部門對用戶列表進行分組
-
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 可以接受一個第二參數實現多級分組。
【示例】根據部門和性別對用戶列表進行分組。
-
/**
-
* 使用 groupingBy() 多級分組
-
* @author pan_junbiao
-
*/
-
-
public void multGroupingByTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//根據部門和性別對用戶列表進行分組
-
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 分組匯總
【示例】根據部門進行分組,匯總各個部門用戶的平均年齡。
-
/**
-
* 使用 groupingBy() 分組匯總
-
* @author pan_junbiao
-
*/
-
-
public void groupCollectTest()
-
{
-
//獲取用戶列表
-
List<User> userList = UserService.getUserList();
-
-
//根據部門進行分組,匯總各個部門用戶的平均年齡
-
Map<String, Double> userMap = userList.stream().collect(Collectors.groupingBy(User::getDepartment, Collectors.averagingInt(User::getAge)));
-
-
//遍歷分組后的結果
-
userMap.forEach((key, value) -> {
-
System.out.println(key + "的平均年齡:" + value);
-
});
-
}
執行結果: