JAVA8 Stream的系列操作,Optional使用---- java养成
Java養成計劃----學習打卡第六十天
內容導航
- Stream回顧
- Stream的中間操作
- 篩選和排序
- 映射
- map和flatmap的區別
- 排序
- Stream的終止操作
- 匹配與查找
- 歸約
- 收集
- Optional類
- 創建Optional類對象
- 判斷Optional容器中是否包含對象
- 獲取Optional容器的對象
Java入門到精通(打卡第六十天)
Java8流操作后續
時間過得真快,學習java2個月了,前面半個月因為復習的事情耽誤了很多進度,還有精力花在算法和刷Java基礎題上,發現很多東西真不會,還有幾個重要的模塊之后要單獨講解,一個就是數據結果后半部分,數據結構前面以前學著就不難,屆時一篇博客總結一下順序表到隊列,著重分析hash表和后面的B樹,還有圖論,hash和線程要分析源碼,比如CurrentHashMap,HashMap,HashSet, ThreadLocal,Simp……很多源碼非常易考
準備正式結束java8,進入數據庫了,增刪查改還是要會的,🚶時間真緊啊
Stream回顧
之前的分享有點久遠了,因為中間很久都沒有使用java8的stream,所以下來簡單回顧一下,首先關于Java8的Lambda表達式和方法引用都是針對函數式接口的,也就是只有一個抽象方法的接口。而stream則是用來處理數據的,比如noSQL就是需要在Java的service中來處理數據,處理數據我們就要使用到流,流的使用分為幾步,是首先是流的創建,流的創建有多種方式,可以通過集合,也可以通過數組,還可以直接創建無限流,Stream類為泛型類,所以要給一個流的數據類型,這里簡單寫一下流的創建
List<Employee> employees = Arrays.asList(new NameListService().getAllEmployees()); Stream<Employee> stream = employees.stream(); //使用集合對象.stream創建 Stream<Employee> parallstream = employees.parallelStream(); //并行流,數據沒有順序,就像多線程一樣 parallstream.forEach(System.out :: println); //對每一個流中的數據進行操作,這里括號里的是函數式接口,使用了方法引用Employee[] employees1 = new NameListService().getAllEmployees(); Stream<Employee> stream1 = Arrays.stream(employees1); //使用數組,對數組的操作可以看成集合,所以使用Arrrays工具的stream來創建 stream1.forEach(t -> System.out.println(t == new Employee(8, "c風", 30, 19800.0))); //使用Lambda表達式//如果不是通過集合或者而數組,還可以直接使用Stream的of方法 Stream<Employee> stream2 = Stream.of(employees1); //用Stream接口中的of方法//還有就是Stream的生成無限流 //1.通過迭代的方式;給一個種子和函數就可以創建無限流 Stream.iterate(2, t -> t * 2).limit(10).forEach(System.out :: println); //2.通過generate【生成】方式,需要提供一個可以自動生成對象的函數 Stream.generate(Math :: random).limit(20).forEach(System.out :: println);首先就是集合對象可以直接使用其的stream或者parallstream方法來創建流;而數組對象可以使用Arrays的stram方法或者Stram接口的of方法都可以;還可以使用 接口中的generate方法來創建流,我們分析前兩種的流
流創建之后就是中間的執行過程,還有一個結束
Stream的中間操作
多個中間操作可以連接起來成為一個流水線,在上一篇博客中我在最后放了一張圖片就是一個Stream的流水線。除非流水線上觸發終止條件,否則中間操作不會執行任何的處理 ,終止操作的時候會一次性全部處理,叫做‘’惰性處理‘這在之前也強調過,和finalize一樣,只能使用一次,想繼續處理只能重新創建流
這里可以看一下效果
stream1.filter(e -> e.getSalary() < 7000).forEach(System.out :: println); //里面是一個判斷的函數式接口,所以這里就寫一個判斷語句 stream1.limit(3).forEach(System.out :: println);這里上面已經有了中間操作和終止操作,下面又再次想要操作這個流
java.lang.IllegalStateException: stream has already been operated upon or closed
程序就直接報錯了🏷
篩選和排序
涉及的方法有幾種
- filter(Predicated p) ------ 接收Lambda,從流中排除某些元素。過濾
1 C神 35 3000.0
2 C云 34 3500.0 程序員 FREE 聯想T4 【6000 】
3 C強 30 3800.0 設計師 FREE 3000.0 戴爾 【NEC17寸 】
4 李L 31 3100.0 程序員 FREE 戴爾 【三星 17寸 】
6 張Q 22 6800.0 程序員 FREE 華碩 【三星 17寸 】
10 丁D 21 6600.0 程序員 FREE 戴爾 【NEC 17寸 】
- distinct() ------ 篩選,通過流的生成元素的hashCode()和equals()方法去除重復元素
- limit(long maxSize) ---- 截斷流,使其元素不超過給定值
1
2
3
- skip(long n) – -------- 跳過元素,返回一個扔掉前n個元素的流,若流中元素不足n,則返回空流,與limit互補
所以剛好互補,比如limit(3)和skip(3), 就剛好是流的前3個和流的除了前3個后面的數據
映射
主要方法都是和map相關,也即是映射
- map(Function f) -------------- 接收一個函數作為參數,該函數會被應用到每一個元素上,并將其變為一個新的元素 就是x變到y】
- mapToDouble(ToDoubleFunction f) ------接收一個函數作為參數,該函數會被應用到每一個元素上,并產生一個新的DoubleStream
- mapToInt(ToIntFunction f) ------- 接收一個函數作為參數,該函數會被應用到每一個元素上,并會產生一個新的IntStream
- mapToLong(ToLongFunction f) -------- 接收一個函數作為參數,該函數會被應用到每一個元素上,產生一個新的LongStream
- flatMap(Function f) --------------------- 接收一個函數作為參數,將流中的每一個值都換成另一個流,然后將所有流連成一個流
map和flatmap的區別
這個有點類似集合的合并,這里舉一個例子
ArrayList list1 = new ArrayList<>(); list1.add(1); list1.add(2);ArrayList list2 = new ArrayList<>(); list2.add(3); list2.add(4); System.out.println(list1); //list1.add(list2); list1.addAll(list2); System.out.println(list1);[1, 2]
[1, 2, [3, 4]]
[1, 2, 3, 4]
這里兩種添加的方式不同,所以獲得結果不同,直接添加表獲得的是一個廣義表,而添加元素則還是一個普通的表
那這個和map又什么關系?🏷
map就類似與add方法
flatmap就類似于addAll方法
也就是普通的map映射如果裝載一個流,則該流是以整體裝入,而flatmap則是將所有的流連接起來
這里來測試一下這兩個方法
public static Stream<Character> fromStringToStream(String str) { ArrayList<Character> list = new ArrayList<>(); for(Character c : str.toCharArray()) {//將字符串變為了字符數組list.add(c); //將數組添加進入集合}return list.stream(); }List<String> list = Arrays.asList("cfeng","clei","cyu","cdian"); //list.stream().map(str -> fromStringToStream(str));//相當于流的流 list.stream().map(StreamTest :: fromStringToStream).forEach(System.out :: println);這里的流經過映射之后每一個元素都變成了一個Stream< Character>,也就是變成了流的流,那么遍歷出來的
java.util.stream.ReferencePipeline$Head@6c629d6e java.util.stream.ReferencePipeline$Head@5ecddf8f java.util.stream.ReferencePipeline$Head@3f102e87 java.util.stream.ReferencePipeline$Head@27abe2cd這不是我們想要得到的結果
那想要獲得數據只能再次嵌套,對每個數據的流都要遍歷
list.stream().map(StreamTest :: fromStringToStream).forEach(s -> {
s.forEach(System.out :: println);
});
這樣之后獲取的是一個一個的字符
而flatmap就簡單一些,識別每一個映射后都變成字符流,那么就直接連接再以前形成新的流
list.stream().flatMap(StreamTest :: fromStringToStream).forEach(System.out :: println);連接之后形成的是字符組成的流,就不再是流的流了
排序
該操作就是可以對流中的數據進行排序
- sorted() ----------- 產生一個新流,按自然順序排序
- sorted(Comparator com) ----------- 產生一個新流,其中按比較器順序排序
排序想到的就是Comparable接口,如果數據的類實現了接口,就可以使用自然排序調用實現的這個接口來排序,如果沒有實現接口,就會報錯
public final class String
implements java.io.Serializable, Comparable,
比如String類就實現了接口,就可以使用自然排序
List<String> list = Arrays.asList("cfeng","clei","cyu","cdian"); list.stream().sorted().forEach(System.out :: println);cdian
cfeng
clei
cyu
沒有實現接口,那么就使用定制排序了,所以就使用Lambda表達式來寫出這個comparator接口就可以了
Employee[] emp = new NameListService().getAllEmployees(); Stream<Employee> stream2 = Arrays.stream(emp); stream2.sorted().forEach(System.out :: println); //沒有實現接口,會報錯 // java.lang.ClassCastException: class pers.Cfeng.groupsheduing.datafield.Programmer cannot be cast to class java.lang.Comparable (pers.Cfeng.groupsheduing.datafield.Programmer is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')那這里就簡單按照年齡排序
Employee[] emp = new NameListService().getAllEmployees(); Stream<Employee> stream2 = Arrays.stream(emp); stream2.sorted( (e1,e2) -> Integer.compare(e1.getAge(), e2.getAge())).forEach(System.out :: println);10 丁D 21 6600.0 程序員 FREE 戴爾 【NEC 17寸 】
6 張Q 22 6800.0 程序員 FREE 華碩 【三星 17寸 】
11 張C 25 7100.0 程序員 FREE 華碩 【三星 17寸 】
9 C雨 26 9800.0 設計師 FREE 5500.0 惠普m6 【5800 】
12 C楊 27 9600.0 設計師 FREE 4800.0 惠普m6 【5800 】
5 雷J 28 10000.0 設計師 FREE 5000.0 佳能 2900 【激光 】
7 柳Y 29 10800.0 設計師 FREE 5200.0 華碩 【三星 17寸 】
3 C強 30 3800.0 設計師 FREE 3000.0 戴爾 【NEC17寸 】
8 C風 30 19800.0 架構師 FREE 15000.0 2500 愛普生20K 【針式 】
13 劉大W 30 19500.0 架構師 FREE 17000.0 3000 HP M401d 【噴墨 】
4 李L 31 3100.0 程序員 FREE 戴爾 【三星 17寸 】
2 C云 34 3500.0 程序員 FREE 聯想T4 【6000 】
1 C神 35 3000.0
可以看到就是按照年齡的大小排序的
Stream的終止操作
stream的終止操作會從流的流水線生成結果,結果可以不是任何流的值,可以是List,Integer,void;流進行終止操作后,不能夠再次使用
終止操作也有幾種類型
匹配與查找
- allMatch(Predicate p) 檢查是否匹配所有元素
- anyMatch(Predicate p) 檢查是否至少匹配一個元素
- noneMatch(Predicate p) 檢查是否沒有匹配元素
- findFirst() 返回第一個元素
- findAny() 返回流中的任意元素
- count() 返回流中元素的總個數
- max
- min
打印的結果就是true
Employee[] emp = new NameListService().getAllEmployees(); Stream<Employee> stream2 = Arrays.stream(emp); Optional<Employee> is = stream2.findFirst(); System.out.println(is);這樣打印的結果為
Optional[1 C神 35 3000.0]
歸約
map和reduce的連接通常稱為map-reduce模式
- reduce(T iden, BinaryOperator b) 可以將流中的元素反復結合起來,得到一個值,返回T
- reduce(BinaryOperator b) 可以流中的元素反復結合起來,得到一個值,返回Optional
Optional[113400.0]
收集
Collectors使用類提供了很多靜態方法方便創建收集器
Collector接口中的方法的實現決定了如何對流執行收集的操作,如收集到Map,List
- collect(Collector c) 將流轉換為其他形式,接收一個Collector接口的實現,用于Stream中的元素做匯總
也就是處理流后,將流收集起來裝到容器中
這里就是放入List中
[5 雷J 28 10000.0 設計師 FREE 5000.0 佳能 2900 【激光 】, 6 張Q 22 6800.0 程序員 FREE 華碩 【三星 17寸 】, 7 柳Y 29 10800.0 設計師 FREE 5200.0 華碩 【三星 17寸 】, 8 C風 30 19800.0 架構師 FREE 15000.0 2500 愛普生20K 【針式 】, 9 C雨 26 9800.0 設計師 FREE 5500.0 惠普m6 【5800 】, 10 丁D 21 6600.0 程序員 FREE 戴爾 【NEC 17寸 】, 11 張C 25 7100.0 程序員 FREE 華碩 【三星 17寸 】, 12 C楊 27 9600.0 設計師 FREE 4800.0 惠普m6 【5800 】, 13 劉大W 30 19500.0 架構師 FREE 17000.0 3000 HP M401d 【噴墨 】]
可以寫成
list.forEach(System.out :: println);這樣子打印就整齊了🏮
Optional類
java8的另外一個變革就是引入了Optaional類,java8很多東西就是互相支持,其實好像最核心的是Stream,Lambda表達式和方法引用的作用就是給其提供支持;而昨天的
Employee[] emp = new NameListService().getAllEmployees(); Stream<Employee> stream2 = Arrays.stream(emp); Optional<Employee> is = stream2.findFirst(); System.out.println(is);使用is來存儲流中的第一個數據就使用到了Optional類,那么Optional類的作用到底是什么呢?
如果寫過很多java代碼,就可以意識到java程序失敗的一個重要的原因就是空指針,在C/C++中,最主要的一個錯誤就是指針問題,比如空指針,比如野指針;
而Optional< T>類(java.util.Optional)是一個 容器類,它可以保存類型T的值,代表這個值存在。或者僅僅保存null ,表示這個值不存在,原來用null表示一個值不存在,現在可以更好的一個值不存在,現在Optional可以更好的避免空指針異常
Optional ----- 可以為null的容器對象,如果值存在,就isPresent方法會返回true,并用get方法返回對象
創建Optional類對象
- Optional.of(T t) 創建一個Optional實例,t必須非空
- Optional.empty() 創建一個空的Optional實例
- Optional.ofNullable(T t) t可以為null
of方法創建對象不能為空,而ofNullable則可以為空
Employee em = null; Optional<Employee> optionaltest = Optional.ofNullable(em); System.out.println(optionaltest);Optional.empty
所以之前我們調取時總是
Cannot invoke “pers.Cfeng.groupsheduing.datafield.Employee.getName()” because “em” is null
而為了保證這個情況要寫很多if語句
if( 非空) —
有了Optional類之后就可以轉換一下方式
這里使用Programmer的getEquipment
//原來的方式來獲取eqt的name public String getEquipmentName(Programmer emp) {if(emp != null) {Equipmemt eqt = emp.getEquipment();if(eqt != null) {return eqt.getName();}}return null; }//使用Optional方式獲取name public String getEquipmentName(Programmer emp1) {//Optional就是一個包裝的類,就像Integer對int進行包裝,Optional對普通的類進行包裝之后能夠避免空指針//判空就先包裝,再解開包裝Optional<Programmer> optionalEmp = Optional.ofNullable(emp1);//使用ofNullable來避免空指針導致的異常Programmmer emp = optionalEmp.orElse(new Programmer(1, "C風", 0, 0, null));Equipment eqt = emp.getEquipment();Optional<Equipment> optionalEqt = Optional.ofNullable(eqt);Equipment eqt1 = optionalEqt.orElse(new Equipment("TP-LINK"));return eqt1.getName(); }判斷Optional容器中是否包含對象
- boolean isPresent() 判斷是否包含對象
- void ifPresent(Consumer< ? super T) consumer) 如果有值,就執行Consumer接口的代碼,該值會作為參數傳給接口
獲取Optional容器的對象
- T get() 如果調用的對象包含值,返回該值,否則拋出異常
- T orElse(T other) 如果有值就返回,否則返回指定的oher對象
- TorElseGet(Supplier< ? extends T> other) 如果有值就將其返回,否則返回Supplier接口指定的對象
- T orElseThrow(Supplier< ? extends T> exceptionSupplier) 如果有值就返回,否則返回Supplier接口提供的異常
1 Cfeng 34 3000.0
所以我們常用的方法時ofNullable和orElse
Optional<Equipment> optionalEqt = Optional.ofNullable(eqt);Equipment eqt1 = optionalEqt.orElse(new Equipment("TP-LINK"));先將可能為空的對象使用Optional的ofNullable包裝,之后使用orElse拆包,之后正常使用,可方便判斷空
總結
以上是生活随笔為你收集整理的JAVA8 Stream的系列操作,Optional使用---- java养成的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【每周一篇】推荐算法之威尔逊区间法
- 下一篇: 安卓手机通过OTG转接头连接U盘(USB