Java8新特性,Stream入門詳解,豐富案例等你細品
作者:添甄
Stream流是JDK8新增用來處理集合、數組、文件等數據,借助Lambda表達式,極大提高編程效率和程序可讀性,同時擁有串行和并行兩種數據處理模式,并行模式可以充分利用多核CPU性能,通過 fork/join 方式拆解任務加速處理。
JDK8的重要更新除去Lambda之外還有Stream,兩者結合使用為操作和計算數據提供了極大的便利。
本篇文章并不打算長篇大論,文章過長會閱讀疲勞,Stream也并不是一兩篇文章可以介紹清楚的,本篇主要介紹Stream的簡單理論加上案例來體會一下Stream的作用,便利和魅力,后續文章會系統講解Stream體系!
Stream是什么
Stream就是【流】的意思,與 java.io包中的輸入流,輸出流是兩個不同的概念
Stream流是JDK8新增用來處理集合、數組、文件等數據,借助Lambda表達式,極大提高編程效率和程序可讀性,同時擁有串行和并行兩種數據處理模式,并行模式可以充分利用多核CPU性能,通過 fork/join 方式拆解任務加速處理。
Stream好處
- 函數式編程:讓Java原本臃腫的代碼變的簡潔,這當然是需要配合Lambda實現
- 高效的并行處理機制,比之前的for循環加if...else,挨個元素處理速度要快上許多
- 具有多種數據的處理實現,比如篩選,去重,轉換,查詢,遍歷等內置操作
Stream特點
- 流與集合、數組、文件不同,不是數據結構,不存儲數據,目的是處理數據,將處理結果返回或者轉換
- 流在計算數據時,如果需要使用到集合中元素,會取出使用,并不修改源數據,流只使用數據一次
- 支持延遲計算,只有等到執行終止操作時才會執行計算,可以降低不必要的CPU資源浪費
Stream操作分類
- 創建流:可以通過集合、數組、IO資源、Stream的構造函數創建
- 中間操作:對數據的計算操作,比如篩選,去重,轉換等操作,一個中間操作返回一個新的Stream,來支持連續計算
- 終止操作:每個流只能有一次終止操作,終止之后流無法使用,會產生一個計算結果,可以根據需求轉換為想要的結果類型
概念先不說那么多,免得云里霧里,接下來我們直接通過案例來使用Stream對集合的操作
案例:
- 獲取運費大于5000元的運單,并放到新集合中
- 將推薦運單按照運費從高到低排序
- 統計最高運費,最低運費,平均運費
- 將運單按照運費從高到低排序,相同者按照距離從高到低排序
- 將運單按貨物類型分類,將運單按貨物類型和目的地分類,將運單按照運費是否高于5000元分為兩部分
接下來通過傳統方式和Stream兩種分別實現,對比不同
提前準備:
import java.math.BigDecimal;
public class Waybill {
// id
private Long id;
// 運單編號
private String wayNo;
// 運費
private BigDecimal price;
// 貨物類型
private String freightType;
// 距離
private BigDecimal distance;
// 目的地
private String endAddress;
public Waybill(Long id, String wayNo, BigDecimal price, String freightType, BigDecimal distance, String endAddress) {
this.id = id;
this.wayNo = wayNo;
this.price = price;
this.freightType = freightType;
this.distance = distance;
this.endAddress = endAddress;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getWayNo() {
return wayNo;
}
public void setWayNo(String wayNo) {
this.wayNo = wayNo;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getFreightType() {
return freightType;
}
public void setFreightType(String freightType) {
this.freightType = freightType;
}
public BigDecimal getDistance() {
return distance;
}
public void setDistance(BigDecimal distance) {
this.distance = distance;
}
public String getEndAddress() {
return endAddress;
}
public void setEndAddress(String endAddress) {
this.endAddress = endAddress;
}
@Override
public String toString() {
return "Waybill{" +
"id=" + id +
", wayNo='" + wayNo + '\'' +
", price=" + price +
", freightType='" + freightType + '\'' +
", distance=" + distance +
", endAddress='" + endAddress + '\'' +
'}';
}
}
1、獲取運費大于5000元的運單,并放到新集合中
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WaybillMain {
private static List<Waybill> waybills = new ArrayList<>();
static {
// 創建數據
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"鋼材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"鋼材",new BigDecimal(300),"鄭州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"廣州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}
public static void main(String[] args){
// 1、傳統寫法
// 獲取運費大于5000元的運單,并放到新集合中
List<Waybill> newWaybills = new ArrayList<Waybill>();
for (Waybill waybill : waybills) {
// 判斷價格大于5000,BigDecimal需要使用compareTo方法比較
// 1:左邊比右邊大,0:相等,-1:右邊比左邊大
if(waybill.getPrice().compareTo(new BigDecimal(5000)) == 1) {
newWaybills.add(waybill);
}
}
// 遍歷
for (Waybill newWaybill : newWaybills) {
System.out.println(newWaybill);
}
// 2、Stream + Lambda寫法
System.out.println("**華麗麗的分割線**");
// 1) 通過集合的stream()方法創建流對象
Stream<Waybill> stream = waybills.stream();
// 2) 通過流對象的方法計算數據,filter:過濾數據
// filter接收一個過濾條件,item為當前操作的元素,比較價格是否大于5000,滿足條件的過濾出來,放到一個新的Stream對象中
Stream<Waybill> waybillStream = stream.filter(item -> item.getPrice().compareTo(new BigDecimal(5000)) == 1);
// 3) 將過濾后的stream轉換為新的集合,調用collect方法即可,toList()轉換為List集合,toSet轉換為Set集合
List<Waybill> collect = waybillStream.collect(Collectors.toList());
// 遍歷,通過方法引用遍歷
collect.forEach(System.out::println);
}
}
運行結果:
解釋:
- filter():方法就是中間操作,意為過濾符合條件的數據,但是這個數據你還不使用,就先不執行
- collect():方法是終結操作,意為要將Stream的計算結果轉換為一個List集合,Stream認為你要用計算結果了,所以會執行計算,之后保存結果到新的集合中
- 計算過程Stream中是不存儲數據的,沒有獲取數據的方法
2、將推薦運單按照運費從高到低排序
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WaybillMain {
private static List<Waybill> waybills = new ArrayList<>();
static {
// 創建數據
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"鋼材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"鋼材",new BigDecimal(300),"鄭州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"廣州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}
public static void main(String[] args) {
// 1、升序排序
List<Waybill> collect1 = waybills.stream().sorted(Comparator.comparing(Waybill::getPrice)).collect(Collectors.toList());
System.out.println("**升序排序**");
collect1.forEach(System.out::println);
// 2、降序排序,調用reversed方法即可降序
List<Waybill> collect2 = waybills.stream().sorted(Comparator.comparing(Waybill::getPrice).reversed()).collect(Collectors.toList());
System.out.println("**降序排序**");
collect2.forEach(System.out::println);
// 3、如果只想獲取運單號
List<String> collect3 = waybills.stream().sorted(Comparator.comparing(Waybill::getPrice)).map(Waybill::getWayNo).collect(Collectors.toList());
System.out.println("**降序只獲取運單號**");
collect3.forEach(System.out::println);
// 4、先按距離,再按運費,通過thenComparing方法做繼續排序
List<Waybill> collect4 = waybills.stream().sorted(Comparator.comparing(Waybill::getDistance).thenComparing(Waybill::getPrice)).collect(Collectors.toList());
System.out.println("**先按距離再按運費**");
collect4.forEach(System.out::println);
// 5、自定義排序
List<Waybill> collect5 = waybills.stream().sorted((o1, o2) -> {
// 排序規則:根據貨物類型排序,相同的根據距離排序
if (o1.getFreightType().equals(o2.getFreightType())) {
return o1.getDistance().compareTo(o2.getDistance());
} else {
return o1.getFreightType().compareTo(o2.getFreightType());
}
}).collect(Collectors.toList());
System.out.println("**自定義排序: 根據貨物類型排序,相同的根據距離排序**");
collect5.forEach(System.out::println);
}
}
運行結果:
3、統計最高運費,最低運費,平均運費
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WaybillMain {
private static List<Waybill> waybills = new ArrayList<>();
static {
// 創建數據
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"鋼材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"鋼材",new BigDecimal(300),"鄭州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"廣州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}
public static void main(String[] args){
// 1、最高運費,通過max方法
Optional<Waybill> max = waybills.stream().max(Comparator.comparing(Waybill::getPrice));
System.out.println("**運費最高**");
System.out.println(max);
// 2、最低運費,通過min方法
Optional<Waybill> min = waybills.stream().min(Comparator.comparing(Waybill::getPrice));
System.out.println("**運費最低**");
System.out.println(min);
// 3、平均運費,通過 Collectors.averagingDouble計算平均值,需要將 BigDecimal轉換為double類型
Double avg = waybills.stream().collect(Collectors.averagingDouble(item -> item.getPrice().doubleValue()));
System.out.println("**平均運費**");
System.out.println(avg);
}
}
運行截圖:
4、將運單按貨物類型分類,將運單按貨物類型和目的地分類,將運單按照運費是否高于5000元分為兩部分
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WaybillMain {
private static List<Waybill> waybills = new ArrayList<>();
static {
// 創建數據
waybills.add(new Waybill(1L,"Y11111111111",new BigDecimal(1000),"鋼材",new BigDecimal(200),"上海市"));
waybills.add(new Waybill(2L,"Y22222222222",new BigDecimal(2000),"鋼材",new BigDecimal(300),"鄭州市"));
waybills.add(new Waybill(3L,"Y33333333333",new BigDecimal(3000),"水泥",new BigDecimal(300),"北京市"));
waybills.add(new Waybill(4L,"Y44444444444",new BigDecimal(4000),"水泥",new BigDecimal(400),"廣州市"));
waybills.add(new Waybill(5L,"Y55555555555",new BigDecimal(5000),"沙子",new BigDecimal(500),"上海市"));
waybills.add(new Waybill(6L,"Y66666666666",new BigDecimal(6000),"板材",new BigDecimal(500),"深圳市"));
waybills.add(new Waybill(7L,"Y77777777777",new BigDecimal(7000),"蔬菜",new BigDecimal(500),"杭州市"));
}
public static void main(String[] args) {
// 1、運單按貨物類型分類
Map<String, List<Waybill>> collect1 = waybills.stream().collect(Collectors.groupingBy(Waybill::getFreightType));
System.out.println("** 運單按貨物類型分類 **");
collect1.forEach((key,value) -> {
System.out.println("key==>" + key + ",value==>" + value);
});
// 2、運單按貨物類型和目的地分類
Map<String, Map<String, List<Waybill>>> collect2 = waybills.stream().collect(Collectors.groupingBy(Waybill::getFreightType, Collectors.groupingBy(Waybill::getEndAddress)));
System.out.println("** 運單按貨物類型和目的地分類 **");
collect2.forEach((key,value) -> {
System.out.println("key==>" + key + ",value==>" + value);
});
// 3、運單按照運費是否高于5000元分為兩部分,這個叫分區了
Map<Boolean, List<Waybill>> collect3 = waybills.stream().collect(Collectors.partitioningBy(item -> item.getPrice().compareTo(new BigDecimal(5000)) == 1));
System.out.println("** 運單按照5000分區 **");
collect3.forEach((key,value) -> {
System.out.println("key==>" + key + ",value==>" + value);
});
}
}
運行截圖:
總結
- Stream的概念、好處、特點和操作分類
- Stream操作集合案例,體會便利之處
- 案例自己寫幾遍,再觀察自己的項目中哪里可以使用Stream替換
責任編輯:武曉燕
來源:
今日頭條