摘要:大家好,我是樂字節(jié)的小樂。需要注意的是很多流操作本身就會返回一個流,所以多個操作可以直接連接起來,如下圖這樣,操作可以進行鏈?zhǔn)秸{(diào)用,并且并行流還可以實現(xiàn)數(shù)據(jù)流并行處理操作。為集合創(chuàng)建并行流。
大家好,我是樂字節(jié)的小樂。說起流,我們會聯(lián)想到手機、電腦組裝流水線,物流倉庫商品包裝流水線等等,如果把手機 ,電腦,包裹看做最終結(jié)果的話,那么加工商品前的各種零部件就可以看做數(shù)據(jù)源,而中間一系列的加工作業(yè)操作,就可以看做流的處理。
Java Se中對于流的操作有輸入輸出IO流,而Java8中引入的Stream 屬于Java API中的一個新成員,它允許你以聲明性方式處理數(shù)據(jù)集合,Stream 使用一種類似 SQL 語句從數(shù)據(jù)庫查詢數(shù)據(jù)的直觀方式來提供一種對 Java 集合運算和表達的高階抽象。 注意這里的流操作可以看做是對集合數(shù)據(jù)的處理。
簡單來說,流是一種數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組、文件等)所生產(chǎn)的元素序列。
源-流會使用一個提供數(shù)據(jù)的源,如集合、數(shù)組或輸入|輸出資源。
從有序集生成流時會保留原有的順序。由列表生成的流,其元素順序與列表一致
元素序列-就像集合一樣,流也提供了一個接口,可以訪問特定元素類型的一組有序值。
數(shù)據(jù)處理操作-流的數(shù)據(jù)處理功能支持類似于數(shù)據(jù)庫的操作(數(shù)據(jù)篩選、過濾、排序等操作)。
流水線-多個流操作本身會返回一個流,多個操作就可以鏈接起來,成為數(shù)據(jù)處理的一道流水線。
二、流 & 集合計算的時期
集合中數(shù)據(jù)都是計算完畢的數(shù)據(jù),例如從數(shù)據(jù)庫中查詢用戶記錄 按用戶id 查詢 降序排列 然后通過list 接收用戶記錄,數(shù)據(jù)的計算已在放入集合前完成
流中數(shù)據(jù)按需計算,按照使用者的需要計算數(shù)據(jù),例如通過搜索引擎進行搜索,搜索出來的條目并不是全部呈現(xiàn)出來的,而且先顯示最符合的前 10 條或者前 20 條,只有在點擊 “下一頁” 的時候,才會再輸出新的 10 條。流的計算也是這樣,當(dāng)用戶需要對應(yīng)數(shù)據(jù)時,Stream 才會對其進行計算處理。
外部迭代與內(nèi)部迭代
把集合比作一個工廠的倉庫的話,一開始工廠硬件比較落后,要對貨物作什么修改,此時工人親自走進倉庫對貨物進行處理,有時候還要將處理后的貨物轉(zhuǎn)運到另一個倉庫中。此時對于開發(fā)者來說需要親自去做迭代,一個個地找到需要的貨物,并進行處理,這叫做外部迭代。
當(dāng)工廠發(fā)展起來后,配備了流水線作業(yè),工廠只要根據(jù)需求設(shè)計出相應(yīng)的流水線,然后工人只要把貨物放到流水線上,就可以等著接收成果了,而且流水線還可以根據(jù)要求直接把貨物輸送到相應(yīng)的倉庫。這就叫做內(nèi)部迭代,流水線已經(jīng)幫你把迭代給完成了,你只需要說要干什么就可以了(即設(shè)計出合理的流水線)。相當(dāng)于 Java8 引入的Stream 對數(shù)據(jù)的處理實現(xiàn)了”自動化”操作。
三、流操作過程整個流操作就是一條流水線,將元素放在流水線上一個個地進行處理。需要注意的是:很多流操作本身就會返回一個流,所以多個操作可以直接連接起來, 如下圖這樣,操作可以進行鏈?zhǔn)秸{(diào)用,并且并行流還可以實現(xiàn)數(shù)據(jù)流并行處理操作。
總的來說,流操作過程分為三個階段:
創(chuàng)建
借助數(shù)據(jù)源創(chuàng)建流對象
中間處理
篩選、切片、映射、排序等中間操作
終止流
匹配、匯總、分組等終止操作
四、流的創(chuàng)建對流操作首先要創(chuàng)建對應(yīng)的流,流的創(chuàng)建集中形式如下:
在 Java 8 中, 集合接口有兩個方法來生成流:
stream()?? 為集合創(chuàng)建串行流。
parallelStream()?? 為集合創(chuàng)建并行流。
示例代碼如下:
public static void main(String[] args) { /** * 定義集合l1 并為集合創(chuàng)建串行流 */ Listl1 = Arrays.asList("周星馳", "周杰倫", "周星星", "周潤發(fā)"); // 返回串行流 l1.stream(); // 返回并行流 l1.parallelStream(); }
上述操作得到的流是通過原始數(shù)據(jù)轉(zhuǎn)換過來的流,除了這種流創(chuàng)建的基本操作外,對于流的創(chuàng)建還有以下幾種方式。
4.2 值創(chuàng)建流Stream.of(T...) : Stream.of("aa", "bb") 生成流 //值創(chuàng)建流 生成一個字符串流 Stream4.3 數(shù)組創(chuàng)建流stream = Stream.of("java8", "Spring", "SpringCloud"); stream.forEach(System.out::println);
根據(jù)參數(shù)的數(shù)組類型創(chuàng)建對應(yīng)的流。
Arrays.stream(T[ ])
Arrays.stream(int[ ])
Arrays.stream(double[ ])
Arrays.stream(long[ ])
/**
這里以int 為例 long double 不再舉例
*/ Stream stream = Arrays.stream(Arrays.asList(10, 20, 30, 40).toArray()); // 根據(jù)數(shù)組索引范圍創(chuàng)建指定Stream stream = Arrays.stream(Arrays.asList(10, 20, 30, 40).toArray(), 0, 2);4.4 文件生成流
stream = Files.lines(Paths.get("C:javajdbc.properties")); System.out.println(stream.collect(Collectors.toList())); // 指定字符集編碼 stream = Files.lines(Paths.get("C:javajdbc.properties"), Charset.forName("utf-8")); System.out.println(stream.collect(Collectors.toList()));4.5 函數(shù)生成流
兩個方法:
iterate : 依次對每個新生成的值應(yīng)用函數(shù)
generate :接受一個函數(shù),生成一個新的值
// 重100 開始 生成偶數(shù)流
Stream.iterate(100, n -> n + 2);
// 產(chǎn)生1-100 隨機數(shù)
Stream.generate(() ->(int) (Math.random() * 100 + 1));
流的中間操作分為三大類:篩選切片、映射、排序。
篩選切片:類似sql 中where 條件判斷的意思,對元素進行篩選操作
映射:對元素結(jié)果進行轉(zhuǎn)換 ,優(yōu)點類似select 字段意思或者對元素內(nèi)容進行轉(zhuǎn)換處理
排序:比較好理解 ,常用sql 中按字段升序 降序操作
流中間操作數(shù)據(jù)準(zhǔn)備(這里以訂單數(shù)據(jù)處理為例)
@Data public class Order { // 訂單id private Integer id; // 訂單用戶id private Integer userId; // 訂單編號 private String orderNo; // 訂單日期 private Date orderDate; // 收貨地址 private String address; // 創(chuàng)建時間 private Date createDate; // 更新時間 private Date updateDate; // 訂單狀態(tài) 0-未支付 1-已支付 2-待發(fā)貨 3-已發(fā)貨 4-已接收 5-已完成 private Integer status; // 是否有效 1-有效訂單 0-無效訂單 private Integer isValid; //訂單總金額 private Double total; } Order order01 = new Order(1, 10, "20190301", new Date(), "上海市-浦東區(qū)", new Date(), new Date(), 4, 1, 100.0); Order order02 = new Order(2, 30, "20190302", new Date(), "北京市四惠區(qū)", new Date(), new Date(), 1, 1, 2000.0); Order order03 = new Order(3, 20, "20190303", new Date(), "北京市-朝陽區(qū)", new Date(), new Date(), 4, 1, 500.0); Order order04 = new Order(4, 40, "20190304", new Date(), "北京市-大興區(qū)", new Date(), new Date(), 4, 1, 256.0); Order order05 = new Order(5, 40, "20190304", new Date(), "上海市-松江區(qū)", new Date(), new Date(), 4, 1, 1000.0); ordersList = Arrays.asList(order01, order02, order03, order04, order05);六、篩選&切片
篩選有效訂單
// 過濾有效訂單
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.forEach(System.out::println);
篩選有效訂單 取第一頁數(shù)據(jù)(每頁2條記錄)
// 過濾有效訂單 取第一頁數(shù)據(jù)(每頁2條記錄)
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.limit(2) .forEach(System.out::println);
篩選訂單集合有效訂單 取最后一條記錄
// 過濾訂單集合有效訂單 取最后一條記錄
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.skip(ordersList.size() - 2) // 跳過前ordersList.size()-2 記錄 .forEach(System.out::println);
篩選有效訂單 取第3頁數(shù)據(jù)(每頁2條記錄)
// 過濾有效訂單 取第3頁數(shù)據(jù)(每頁2條記錄) 并打印到控制臺
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.skip((3 - 1) * 2) .limit(2) .forEach(System.out::println);
篩選無效訂單去除重復(fù)訂單號記錄
// 過濾無效訂單 去除重復(fù)訂單號記錄 重寫Order equals 與 hashCode 方法
ordersList.stream().filter((order) -> order.getIsValid() == 0)
.distinct() .forEach(System.out::println);七、映射
過濾有效訂單,獲取所有訂單編號
//過濾有效訂單,獲取所有訂單編號
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.map((order) -> order.getOrderNo()) .forEach(System.out::println);
過濾有效訂單 ,并分離每個訂單下收貨地址市區(qū)信息
ordersList.stream().map(o -> o.getAddress()
.split("-")) .flatMap(Arrays::stream) .forEach(System.out::println);八、排序
過濾有效訂單 根據(jù)用戶id 進行排序
//過濾有效訂單 根據(jù)用戶id 進行排序
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.sorted((o1, o2) -> o1.getUserId() - o2.getUserId()) .forEach(System.out::println);
//或者等價寫法
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.sorted(Comparator.comparingInt(Order::getUserId)) .forEach(System.out::println);
過濾有效訂單 ,根據(jù)訂單狀態(tài)排序 如果訂單狀態(tài)相同根據(jù)訂單創(chuàng)建時間排序
//過濾有效訂單 如果訂單狀態(tài)相同 根據(jù)訂單創(chuàng)建時間排序 反之根據(jù)訂單狀態(tài)排序
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.sorted((o1, o2) -> { if (o1.getStatus().equals(o2.getStatus())) { return o1.getCreateDate().compareTo(o2.getCreateDate()); } else { return o1.getStatus().compareTo(o2.getStatus()); }}) .forEach(System.out::println);
// 等價形式
ordersList.stream().filter((order) -> order.getIsValid() == 1)
.sorted(Comparator.comparing(Order::getCreateDate) .thenComparing(Comparator.comparing(Order::getStatus))) .forEach(System.out::println);九、流的終止操作
終止操作會從流的流水線生成結(jié)果。其結(jié)果是任何不是流的值,比如常見的List、 Integer,甚 至void等結(jié)果。對于流的終止操作,分為以下三類:十、查找與匹配
篩選有效訂單 匹配是否全部為已支付訂單
// 篩選有效訂單 匹配是否全部為已支付訂單
System.out.println("allMatch匹配結(jié)果:" +
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .allMatch((o) -> o.getStatus() != 0) );
篩選有效訂單 匹配是否存在未支付訂單
// 篩選有效訂單 匹配是否存在未支付訂單
System.out.println("anyMatch匹配結(jié)果:" +
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .anyMatch((o) -> o.getStatus() == 0) );
篩選有效訂單 全部未完成訂單
// 篩選有效訂單 全部未完成訂單
System.out.println("noneMatch匹配結(jié)果:" +
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .noneMatch((o) -> o.getStatus() == 5) );
篩選有效訂單 返回第一條訂單
// 篩選有效訂單 返回第一條訂單
System.out.println("findAny匹配結(jié)果:"+ ordersList.stream() .filter((order) -> order.getIsValid() == 1) .findAny() .get() );
篩選所有有效訂單 返回訂單總數(shù)
// 篩選所有有效訂單 返回訂單總數(shù)
System.out.println("count結(jié)果:" + ordersList.stream() .filter((order) -> order.getIsValid() == 1) .count() );
篩選有效訂單 返回金額最大訂單金額
// 篩選有效訂單 返回金額最大訂單金額
System.out.println("訂單金額最大值:" + ordersList.stream() .filter((order) -> order.getIsValid() == 1) .map(Order::getTotal) .max(Double::compare) .get() );
篩選有效訂單 返回金額最小訂單金額
// 篩選有效訂單 返回金額最小訂單金額
System.out.println("訂單金額最小值:" + ordersList.stream() .filter((order) -> order.getIsValid() == 1) .map(Order::getTotal) .min(Double::compare) .get() );十一、歸約&收集 11.1 歸約
將流中元素反復(fù)結(jié)合起來,得到一個值的操作
計算有效訂單總金額
// 計算有效訂單總金額
System.out.println("有效訂單總金額:" + ordersList.stream() .filter((order) -> order.getIsValid() == 1) .map(Order::getTotal) .reduce(Double::sum) .get() );11.2 Collector數(shù)據(jù)收集
將流轉(zhuǎn)換為其他形式,coollect 方法作為終端操作, 接收一個Collector接口的實現(xiàn),用于給Stream中元素做匯總的方法。最常用的方法,把流中所有元素收集到一個 List, Set 或 Collection中
11.3 集合收集常用集合收集方法 toList、toSet、toCollection、toMap等
篩選所有有效訂單 并收集訂單列表
// 篩選所有有效訂單并收集訂單列表
ordersList.stream()
.filter((order) -> order.getIsValid() == 1) .collect(Collectors.toList()) .forEach(System.out::println);
篩選所有有效訂單并收集訂單號與訂單金額
// 篩選所有有效訂單 并收集訂單號 與 訂單金額
Map
collect(Collectors.toMap(Order::getOrderNo, Order::getTotal));
// java8 下對map進行遍歷操作 如果 Map的Key重復(fù),會報錯
map.forEach((k,v)->{
System.out.println("k:"+k+":v:"+v);
});
十二、匯總匯總操作在Stream流操作比較常見,比如計算總數(shù),求平均等操作,常用方法如下:
相關(guān)操作如下
篩選所有有效訂單 返回訂單總數(shù)
System.out.println("count結(jié)果:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .collect(Collectors.counting()) );
System.out.println("count結(jié)果:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .count() );
返回訂單總金額
System.out.println("訂單總金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .collect(Collectors.summarizingDouble(Order::getTotal)) );
System.out.println("訂單總金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .mapToDouble(Order::getTotal) .sum() );
System.out.println("訂單總金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .map(Order::getTotal) .reduce(Double::sum) .get() );
返回用戶id=20 有效訂單平均每筆消費金額
System.out.println("用戶id=20 有效訂單平均每筆消費金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .filter((order -> order.getUserId()==20)) .collect(Collectors.averagingDouble(Order::getTotal)) );
System.out.println("用戶id=20 有效訂單平均每筆消費金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .filter((order -> order.getUserId()==20)) .mapToDouble(Order::getTotal) .average() .getAsDouble() );
System.out.println("用戶id=20 有效訂單平均每筆消費金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .filter((order -> order.getUserId()==20)) .collect(Collectors.summarizingDouble(Order::getTotal)) .getAverage() );
篩選所有有效訂單 并計算訂單總金額
System.out.println("訂單總金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .collect(Collectors.summingDouble(Order::getTotal)) );十三、最值
篩選所有有效訂單 并計算最小訂單金額
System.out.println("最小訂單金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .map(Order::getTotal) .collect(Collectors.minBy(Double::compare)) );
篩選所有有效訂單 并計算最大訂單金額
// 篩選所有有效訂單 并計算最大訂單金額
System.out.println("最大訂單金額:"+
ordersList.stream() .filter((order) -> order.getIsValid() == 1) .map(Order::getTotal) .collect(Collectors.maxBy(Double::compare)) );十四、分組&分區(qū) 14.1 分組
groupingBy 用于將數(shù)據(jù)分組,最終返回一個 Map 類型 ,groupingBy 第二參數(shù)用于實現(xiàn)多級分組
根據(jù)有效訂單支付狀態(tài)進行分組操作
Map
.filter((order) -> order.getIsValid() == 1) .collect(Collectors.groupingBy(Order::getStatus));
g01.forEach((status,order)->{
System.out.println("----------------"); System.out.println("訂單狀態(tài):"+status); order.forEach(System.out::println);
});
篩選有效訂單,根據(jù)用戶id 和 支付狀態(tài)進行分組
Map
.filter((order) -> order.getIsValid() == 1) .collect(Collectors.groupingBy(Order::getUserId, Collectors.groupingBy((o)->{ if(o.getStatus()==0){ return "未支付"; }else if (o.getStatus()==1){ return "已支付"; }else if (o.getStatus()==2){ return "待發(fā)貨"; }else if (o.getStatus()==3){ return "已發(fā)貨"; }else if (o.getStatus()==4){ return "已接收"; } else{ return "已完成"; } } )) );
g02.forEach((userId,m)->{
System.out.println("用戶id:"+userId+"-->有效訂單如下:"); m.forEach((status,os)->{ System.out.println("狀態(tài):"+status+"---訂單列表如下:"); os.forEach(System.out::println); }); System.out.println("-----------------------");
});
14.2 分區(qū)分區(qū)與分組的區(qū)別在于,分區(qū)是按照 true 和 false 來分的,因此partitioningBy 接受的參數(shù)的 lambda 也是?T -> boolean
分區(qū)操作-篩選訂單金額>1000 的有效訂單
Map
.filter((order) -> order.getIsValid() == 1) .collect(Collectors.partitioningBy((o)->o.getTotal()>1000));
g03.forEach((b,os)->{
System.out.println("分區(qū)結(jié)果:"+b+"--列表結(jié)果:"); os.forEach(System.out::println);
});
拼接操作-篩選有效訂單并進行拼接
String orderStr=ordersList.stream()
.filter((order) -> order.getIsValid() == 1) .map(Order::getOrderNo) .collect(Collectors.joining(","));
System.out.println(orderStr);
十五、流的應(yīng)用Java8引入Stream流操作,使得對元素的處理更加的方便快捷,通過Stream提供的相關(guān)方法很好的結(jié)合Lambda、函數(shù)式接口、方法引用等相關(guān)內(nèi)容,使得流的處理相比較原始集合處理代碼極大簡化,Stream支持函數(shù)的鏈?zhǔn)秸{(diào)用,代碼上更加緊湊同時Stream支持的元素的并行化處理提高了程序的執(zhí)行性能。對于Stream流的應(yīng)用通常在集合元素數(shù)據(jù)處理上特別是對元素需要進行多次處理的情況,同時對于函數(shù)式編程的味道更加濃重,也是以后開發(fā)的一個趨勢。
好了,Java8核心特性之Stream流就介紹到這里了,應(yīng)該是非常詳盡了,希望大家喜歡,多多關(guān)注小樂,小樂將會給大家?guī)砀嗟募几韶?。樂字?jié)祝大家端午快樂,學(xué)習(xí)快樂!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/74876.html
摘要:語法中接口可以包含實現(xiàn)方法,需要使用修飾,此類方法稱為默認(rèn)方法。核心特性接口默認(rèn)方法就介紹到這里了,后續(xù)小樂會繼續(xù)講述核心特性。 JAVA8已經(jīng)發(fā)布很久,是自java5(2004年發(fā)布)之后Oracle發(fā)布的最重要的一個版本。其中包括語言、編譯器、庫、工具和JVM等諸多方面的新特性,對于國內(nèi)外互聯(lián)網(wǎng)公司來說,Java8是以后技術(shù)開發(fā)的趨勢。這里主要講解在開發(fā)中幾個核心的新特性。(主要從...
摘要:大家好,上一篇小樂給大家講述了樂字節(jié)核心特性表達式,點擊回顧。接下來繼續(xù)核心特性之函數(shù)式接口。感謝大家欣賞小樂帶來的核心特性之函數(shù)式接口,接下來還會更多核心技術(shù)講解,請關(guān)注樂字節(jié)如需要視頻課程,請搜索樂字節(jié)騰訊課堂 大家好,上一篇小樂給大家講述了《樂字節(jié)-Java8核心特性-Lambda表達式》,點擊回顧。接下來繼續(xù):Java8核心特性之函數(shù)式接口。 什么時候可以使用Lambda?通常...
摘要:大家好,我是樂字節(jié)的小樂,上一次我們說到了核心特性之函數(shù)式接口,接下來我們繼續(xù)了解又一核心特性方法引用。方法引用是一種更簡潔易懂的表達式。感謝光臨閱讀小樂的,敬請關(guān)注樂字節(jié)后續(xù)將繼續(xù)講述等前沿知識技術(shù)。 大家好,我是樂字節(jié)的小樂,上一次我們說到了Java8核心特性之函數(shù)式接口,接下來我們繼續(xù)了解Java8又一核心特性——方法引用。 showImg(https://segmentfaul...
摘要:需要注意的是很多流操作本身就會返回一個流,所以多個操作可以直接連接起來,如下圖這樣,操作可以進行鏈?zhǔn)秸{(diào)用,并且并行流還可以實現(xiàn)數(shù)據(jù)流并行處理操作。為集合創(chuàng)建并行流。 上一篇文章,小樂給大家介紹了《Java8新特性之方法引用》,下面接下來小樂將會給大家介紹Java8新特性之Stream,稱之為流,本篇文章為上半部分。 1、什么是流? Java Se中對于流的操作有輸入輸出IO流,而Jav...
摘要:使用表達式,使得應(yīng)用變得簡潔而緊湊。很多語言等從設(shè)計之初就支持表達式。表達式的參數(shù)與函數(shù)式接口內(nèi)方法的參數(shù),返回值類型相互對應(yīng)。更多教程和資料請上騰訊課堂樂字節(jié) showImg(https://segmentfault.com/img/bVbtotg?w=935&h=345); Java8 引入Lambda表達式,允許開發(fā)者將函數(shù)當(dāng)成參數(shù)傳遞給某個方法,或者把代碼本身當(dāng)作數(shù)據(jù)進行處理。...
閱讀 3388·2023-04-25 22:47
閱讀 3876·2021-10-11 10:59
閱讀 2364·2021-09-07 10:12
閱讀 4350·2021-08-11 11:15
閱讀 3490·2019-08-30 13:15
閱讀 1812·2019-08-30 13:00
閱讀 1027·2019-08-29 14:02
閱讀 1740·2019-08-26 13:57