前言
接上篇文章 java8 新特性 由於上篇過於龐大,使得重點不夠清晰,本篇單獨拿出 java8 的 Stream 重點說明 ,並做了點補充。
基本說明
- Stream 是基於 java8 的 lambda 表達式的,如果不清楚 lambda 表達式,可以查看我的上篇文章Lambda 表達式和函數式接口快速理解
- Stream 把要處理的元素看做一種流,流在管道中傳輸,可以在管道的節點上處理數據,包含過濾,去重,排序,映射,聚合,分組等。
- Stream 分為中間操作和后期操作,中期操作會形成一個新的 Stream ,但不會馬上對數據進行處理,到后期操作時,再遍歷整個集合;可以沒有中期操作直接后期操作。
創建流的方式
- 使用
java.util.Collection
接口的默認方法stream
或者parallelStream
- 使用
java.util.Arrays
的方法stream
將數組變成流
中期操作和后期操作
Stream 分為中間操作和后期操作,中期操作會形成一個新的 Stream ,但不會馬上對數據進行處理,到后期操作時,再遍歷整個集合;可以沒有中期操作直接后期操作。
中期操作
- map 和 map 之類的,用於映射一種類型到另一種類型
- filter 用於過濾掉一些不符合要求的元素
- distinct 用於排重
- sorted 用於排序
- flatMap 用於將流扁平化
后期操作
forEach,collect,reduce,anyMatch,allMatch,noneMatch,findFirst 等;
其中屬 collect 最為常用,還有一個專門用於 collect 的 Collectors 類,可以用於將集合轉成 List,Set,Map 等
代碼示例
數據准備
- 准備一個用於下面例子測試的對象
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Vehicle {
//車架號
private String vin;
// 車主手機號
private String phone;
// 車主姓名
private String name;
// 所屬車租車公司
private Integer companyId;
// 個人評分
private Double score;
//安裝的設備列表imei,使用逗號分隔
private String deviceNos;
}
- 准備一些車輛數據
static List<Vehicle> vehicles = new ArrayList<>();
@Before
public void init(){
List<String> imeis = new ArrayList<>();
for (int i = 0; i <5 ; i++) {
List<String> singleVehicleDevices = new ArrayList<>();
for (int j = 0; j < 3; j++) {
String imei = RandomStringUtils.randomAlphanumeric(15);
singleVehicleDevices.add(imei);
}
imeis.add(StringUtils.join(singleVehicleDevices,','));
}
vehicles.add(new Vehicle("KPTSOA1K67P081452","17620411498","9420",1,4.5,imeis.get(0)));
vehicles.add(new Vehicle("KPTCOB1K18P057071","15073030945","張玲",2,1.4,imeis.get(1)));
vehicles.add(new Vehicle("KPTS0A1K87P080237","19645871598","sanri1993",1,3.0,imeis.get(2)));
vehicles.add(new Vehicle("KNAJC526975740490","15879146974","李種",1,3.9,imeis.get(3)));
vehicles.add(new Vehicle("KNAJC521395884849","13520184976","袁紹",2,4.9,imeis.get(4)));
}
forEach 遍歷Collection 數據
vehicles.forEach(vehicle -> System.out.println(vehicle));
//這樣就可以遍歷打印
vehicles.forEach(System.out::println);
forEach 遍歷 Map 數據
Map<String,Integer> map = new HashMap<>();
map.put("a",1);map.put("b",2);map.put("c",3);
map.forEach((k,v) -> System.out.println("key:"+k+",value:"+v));
filter 數據過濾
// 去掉評分為 3 分以下的車
List<Vehicle> collect = vehicles.stream().filter(vehicle -> vehicle.getScore() >= 3).collect(Collectors.toList());
map 對象映射
對一個 List<Object>
大部分情況下,我們只需要列表中的某一列,或者需要把里面的每一個對象轉換成其它的對象,這時候可以使用 map 映射,示例:
// 取出所有的車架號列表
List<String> vins = vehicles.stream().map(Vehicle::getVin).collect(Collectors.toList());
groupBy 按照某個屬性進行分組
// 按照公司 Id 進行分組
Map<Integer, List<Vehicle>> companyVehicles = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId));
// 按照公司分組求司機打分和
Map<Integer, Double> collect = vehicles.stream().collect(Collectors.groupingBy(Vehicle::getCompanyId, Collectors.summingDouble(Vehicle::getScore)));
sort 按照某個屬性排序 ,及多列排序
// 單列排序
vehicles.sort((v1,v2) -> v2.getScore().compareTo(v1.getScore()));
// 或使用 Comparator 類來構建比較器,流處理不會改變原列表,需要接收返回值才能得到預期結果
List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()).collect(Collectors.toList());
// 多列排序,score 降序,companyId 升序排列
List<Vehicle> collect = vehicles.stream().sorted(Comparator.comparing(Vehicle::getScore).reversed()
.thenComparing(Comparator.comparing(Vehicle::getCompanyId)))
.collect(Collectors.toList());
flatMap 扁平化數據處理
// 查出所有車綁定的所有設備
List<String> collect = vehicles.stream().map(vehicle -> {
String deviceNos = vehicle.getDeviceNos();
return StringUtils.split(deviceNos,',');
}).flatMap(Arrays::stream).collect(Collectors.toList());
flatMap 很適合 List<List>
或 List<object []>
這種結構,可以當成一個列表來處理;像上面的設備列表,在數據庫中存儲的結構就是以逗號分隔的數據,而車輛列表又是一個列表數據。
將 List 數據轉成 Map
// 將 List 轉成 Map ; key(vin) == > Vehicle
Map<String, Vehicle> vinVehicles = vehicles.stream().collect(Collectors.toMap(Vehicle::getVin, vehicle -> vehicle));
mapReduce 數據處理
// 對所有司機的總分求和
Double reduce = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);
求百分比
// 總的分值
Double totalScore = vehicles.stream().parallel().map(Vehicle::getScore).reduce(0d, Double::sum);
// 查看每一個司機占的分值比重
List<String> collect = vehicles.stream()
.mapToDouble(vehicle -> vehicle.getScore() / totalScore)
.mapToLong(weight -> (long) (weight * 100))
.mapToObj(percentage -> percentage + "%")
.collect(Collectors.toList());
anyMatch/allMatch/noneMatch 匹配操作
- anyMatch 只要有元素匹配,即返回真
- allMatch 要求所有的元素都匹配
- noneMatch 要求沒有一個元素匹配
// 檢查是否有姓李的司機 true
boolean anyMatch = vehicles.stream().anyMatch(vehicle -> vehicle.getName().startsWith("李"));
// 檢查是否所有司機的評分都大於 3 分 false
boolean allMatch = vehicles.stream().allMatch(vehicle -> vehicle.getScore() > 3);
// 檢查是否有 3 公司的特務 true
boolean noneMatch = vehicles.stream().noneMatch(vehicle -> vehicle.getCompanyId() == 3);
一點小推廣
創作不易,希望可以支持下我的開源軟件,及我的小工具,歡迎來 gitee 點星,fork ,提 bug 。
Excel 通用導入導出,支持 Excel 公式
博客地址:https://blog.csdn.net/sanri1993/article/details/100601578
gitee:https://gitee.com/sanri/sanri-excel-poi
使用模板代碼 ,從數據庫生成代碼 ,及一些項目中經常可以用到的小工具
博客地址:https://blog.csdn.net/sanri1993/article/details/98664034
gitee:https://gitee.com/sanri/sanri-tools-maven