1. Stream intermediate operations
The intermediate operations of Stream refer to the processing operations of data in the stream chain, including filter filtering, map mapping conversion, flatMap merging, distinct deduplication, sorted sorting, etc. operate. These operations will return a new Stream object, which can perform complex data processing by chaining multiple intermediate operations. It should be noted that the intermediate operation needs to have a termination operation to be triggered.
The following explains the common intermediate operations of Stream by category.
1.1. Filter: Filter out elements that meet the conditions
The filter() method is often used to implement data filtering, that is, it can filter out elements that meet the specified conditions from data sources such as collections and arrays, and Return a new stream.
Suppose there is a list of blacklisted mobile phone numbers, and you need to filter out all elements starting with "133", then you can implement it through filter() -
//將數組轉換為一個字符串列表 List<String> numbers = Arrays.asList("13378520000","13278520000","13178520000","13358520000"); //通過stream()方法創(chuàng)建一個流,接著使用filter()方法過濾出前綴為“133”的元素,最終通過collect() 方法將結果收集到一個新列表中 List<String> filterdNumbers = numbers.stream().filter(s -> s.startsWith("133")).collect(Collectors.toList()); System.out.println(filterdNumbers); //打印結果:[13378520000, 13358520000]
1.2, map: Mapping conversion elements
The map() method is used to map each element in the stream, convert it to another element or extract the information in it, and return a new stream.
According to the following two cases, learn map() to convert an element into another element and extract the information in the element -
1.2.1. Converting elements
Assume there is a list of mobile phone number characters, and you need to determine the location of the mobile phone number based on the first 7 digits. Then you need to obtain the first 7 substrings of all mobile phone numbers. You can use the map() method to achieve this:
List<String> numbers = Arrays.asList("13378520000","13278520000","13178520000","13558520000"); //通過stream()方法創(chuàng)建一個流,使用map()方法將每個字符串轉換為截取前7位的字符,最后使用collect()方法將結果收集到一個新列表中 List<String> filterdNumbers = numbers.stream().map(s -> s.substring(0,7)).collect(Collectors.toList()); System.out.println(filterdNumbers); //打印結果:[1337852, 1327852, 1317852, 1355852]
1.2.2. Extract element information
Assume there is a list of user objects, and we need to extract the mobile phone number of each object. This can be achieved using the map() method:
List<People> peopleList = Arrays.asList( new People("王二","13378520000"), new People("李二","13278520000"), new People("張四","13178520000") ); //通過stream()方法創(chuàng)建一個流,使用map()方法提取每個用戶的手機號,最后使用collect()方法將結果收集到一個新列表中 List<String> tel = peopleList.stream().map(People::getTel).collect(Collectors.toList()); System.out.println(tel); //打印結果:[13378520000, 13278520000, 13178520000]
1.3. flatMap: Merge multiple streams into one stream
The flatMap() method can implement many-to-many mapping, or merge multiple lists into one list operation.
1.3.1. Implement many-to-many mapping
Assume that there are two sets of balance lists A and B, and each element of group A needs to be matched with all elements of group B. The elements are added sequentially, and you can use flatMap to implement the many-to-many mapping -
List<Integer> listA = Arrays.asList(1, 2, 3); List<Integer> listB = Arrays.asList(4, 5, 6); List<Integer> list = listA.stream().flatMap(a -> listB.stream().map(b -> a +b)).collect(Collectors.toList()); System.out.println(list); //打印結果: [5, 6, 7, 6, 7, 8, 7, 8, 9]
1.3.2. Merge multiple lists into one list
Suppose there is a list containing multiple mobile phone number string lists. Now you need to merge all mobile phone number strings into a list. You can use the flatMap() method to achieve this:
List<List<String>> listOfLists = Arrays.asList( Arrays.asList("13378520000", "13278520000"), Arrays.asList("13178520000", "13558520000"), Arrays.asList("15138510000", "15228310000") ); List<String> flatMapList = listOfLists.stream().flatMap(Collection::stream).collect(Collectors.toList()); System.out.println(flatMapList); //打印結果:[13378520000, 13278520000, 13178520000, 13558520000, 15138510000, 15228310000]
1.4. distinct: remove duplicate elements
The distinct() method can be used to remove duplicate elements in the stream and generate a list without duplicates.
Suppose there is a list containing repeated mobile phone number strings, you can use distinct() to deduplicate operations -
List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000"); List<String> disNumbers = numbers.stream().distinct().collect(Collectors.toList()); System.out.println(disNumbers); //打印結果:[13378520000, 15138510000, 13178520000]
Note that distinct is used to deduplicate flows. When operating, you need to make sure that the elements in the stream implement the equals() and hashCode() methods, because these two methods are the standards for judging whether two objects are equal.
1.5. sorted: Sorting elements
The sorted() method is used to sort elements in the stream.
Assume that a group of People objects need to be sorted by age. The following is sorted in ascending order and descending order respectively -
1.5.1, ascending order
By default, it is sorted in ascending order——
List<People> peopleList = Arrays.asList( new People("王二",20), new People("李二",30), new People("張四",31) ); List<People> newpeopleList=peopleList.stream().sorted(Comparator.comparing(People::getAge)).collect(Collectors.toList()); //打印結果 newpeopleList.stream().forEach(System.out::println);
Print results:
People{name='王二', age=20}
People {name='Li Er', age=30}
People{name='Zhang Si', age=31}
1.5.2, descending order
Use the reversed() method to sort in reverse order, that is, sort in ascending order in reverse order——
List<People> peopleList = Arrays.asList( new People("王二",20), new People("李二",30), new People("張四",31) ); List<People> newpeopleList = peopleList.stream().sorted(Comparator.comparing(People::getAge).reversed()).collect(Collectors.toList()); //打印結果 newpeopleList.stream().forEach(System.out::println);
Print results:
##1.6 , peek: View the information of each element, but do not modify the status of the elements in the stream peek() method is used to view the elements in the stream without modifying the status of the elements in the stream. You can use Use at any stage will not affect the flow operation, nor will it terminate the flow operation.People{name=' Zhang Si', age=31}
People{name='Li Er', age=30}
People{name='Wang Er', age=20}
List<String> telList = Arrays.asList("13378520000","13278520000","13178520000","13558520000"); telList.stream().peek(t -> System.out.println(t)) .map(t -> t.substring(0,3)) .peek(t -> System.out.println(t)) .collect(Collectors.toList());Print results: Very similar, Both can be used to traverse elements in a stream, but there are major differences between the two. The main point is that forEach is a termination operation in the stream. Once it is called, it means that the Stream has been processed and no more operations can be performed. For example, operations such as map and filter cannot be performed on the stream after forEach, but peek The method is OK. As can be seen from the above case, after calling peek for the first time to print an element, the element can also be followed by a map operation to intercept the first three digits of the string. This is the biggest difference between the peek() method and forEach.
1.7、limit 和 skip:截取流中的部分元素
limit()和skip()都是用于截取Stream流中部分元素的方法,兩者區(qū)別在于,limit()返回一個包含前n個元素的新流,skip()則返回一個丟棄前n個元素后剩余元素組成的新流。
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; System.out.print("取數組前5個元素:"); Arrays.stream(arr).limit(5).forEach(n -> System.out.print(n + " ")); // 輸出結果為:1 2 3 4 5 System.out.print("跳過前3個元素,取剩余數組元素:"); Arrays.stream(arr).skip(3).forEach(n -> System.out.print(n + " ")); // 輸出結果為:4 5 6 7 8 9 10二、Stream終止操作
Stream的終止操作是指執(zhí)行Stream流鏈中最后一個步驟,到這一步就會結束整個流處理。在Java8中,Stream終止操作包括forEach、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst和findAny等。這些終止操作都有返回值。需要注意一點是,如果沒有執(zhí)行終止操作的話,Stream流是不會觸發(fā)執(zhí)行的,例如,一個沒有終止操作的peek()方法代碼是不會執(zhí)行進而打印——
list.stream().peek(t -> System.out.println("ddd"))當加上終止操作話,例如加上collect,就會打印出“ddd”——
list.stream().peek(t -> System.out.println("ddd")).collect(Collectors.toList());下面按類別分別講解各個終止操作的使用。
2.1、forEach:遍歷流中的每個元素
該forEach前面已經提到,這里不做過多介紹。
2.2、count:統(tǒng)計流中元素的數量
count可以統(tǒng)計流中元素的數量并返回結果。
假設有一個包含多個手機號字符串的列表,需要統(tǒng)計去重后的手機號數量,就可以使用count方法——
List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000"); long count = numbers.stream() .distinct()//去重 .count();//統(tǒng)計去重后的手機號 System.out.println(count); //打印結果:32.3、reduce:將流中的所有元素歸約成一個結果
reduce()可以將流中的所有元素根據指定規(guī)則歸約成一個結果,并將該結果返回。
常用語法格式如下:
Optional<T> result = stream.reduce(BinaryOperator<T> accumulator);可見,reduce方法會返回一個Optional類型的值,表示歸約后的結果,需要通過get()方法獲取Optional里的值。
假設有一個包含多個手機號字符串的List列表,需要在去重之后,再將列表所有字符串拼按照逗號間隔接成一個字符串返回,那么就可以通過reduce來實現(xiàn)——
List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000"); Optional result = numbers.stream() .distinct() //去重 .reduce((a ,b) -> a+","+b);//指定規(guī)則為,相臨兩個字符通過逗號“,”間隔 System.out.println(result.get()); //打印結果:13378520000,15138510000,131785200002.4、collect:將流中的元素收集到一個容器中,并返回該容器
collect的作用是將流中的元素收集到一個新的容器中,返回該容器。打個比喻,它就像一個采摘水果的工人,負責將水果一個個采摘下來,然后放進一個籃子里,最后將籃子交給你。我在前面的案例當中,基本都有用到collect,例如前面2.1的filter過濾用法中的List filterdNumbers = numbers.stream().filter(s -> s.startsWith("133")).collect(Collectors.toList()),就是將過濾出前綴為“133”的字符串,將這些過濾處理后的元素交給collect這個終止操作。這時collect就像采摘水果的員工,把采摘為前綴“133”的“水果”通過toList()方法收集到一個新的List容器當中,然后交給你。最后你就可以得到一個只裝著前綴為“133”的元素集合。
在Java8的collect方法中,除里toList()之外,還提供了例如toSet,toMap等方法滿足不同的場景,根據名字就可以知道,toSet()返回的是一個Set集合,toMap()返回的是一個Map集合。
2.5、min 和 max:找出流中的最小值和最大值
min和max用來查找流中的最小值和最大值。
假設需要在查找出用戶列表中年齡最小的用戶,可以按照以下代碼實現(xiàn)——
List<People> peopleList = Arrays.asList( new People("王二",20), new People("李二",30), new People("張四",31) ); //查找年齡最小的用戶,若沒有則返回一個null People people = peopleList.stream().min(Comparator.comparing(People::getAge)).orElse(null); System.out.println(people); //打印結果:People{name='王二', age=20}max的用法類似,這里不做額外說明。
2.6、anyMatch、allMatch 和 noneMatch:判斷流中是否存在滿足指定條件的元素
2.6.1、anyMatch
anyMatch用于判斷,如果流中至少有一個元素滿足給定條件,那么返回true,反之返回false,即 true||false為true這類的判斷。
假設在一個手機號字符串的List列表當中,判斷是否包含前綴為“153”的手機號,就可以使用anyMatch——
List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15338510000"); boolean hasNum = numbers.stream().anyMatch(n -> n.startsWith("153")); System.out.println(hasNum); //打印結果:true2.6.2、allMatch
allMatch用于判斷,流中的所有元素是否都滿足給定條件,滿足返回true,反之false,即true&&false為false這類判斷。
假設在一個手機號字符串的List列表當中,判斷手機號是否都滿足前綴為“153”的手機號,就可以用allMatch——
List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15338510000"); boolean hasNum = numbers.stream().allMatch(n -> n.startsWith("153")); System.out.println(hasNum); //打印結果:false2.6.3、noneMatch
noneMatch用于判斷,如果流中沒有任何元素滿足給定的條件,返回true,如果流中有任意一個條件滿足給定條件,返回false,類似!true為false的判斷。
假設在一個手機號字符串的List列表當中,判斷手機號是否都不滿足前綴為“153”的手機號,就可以用noneMatch——
List<String> numbers = Arrays.asList("13378520000", "15138510000","13178520000", "1238510000"); //numbers里沒有前綴為“153”的手機號 boolean hasNum = numbers.stream().noneMatch(n -> n.startsWith("153")); System.out.println(hasNum); //打印結果:true這三個方法其實存在一定互相替代性,例如在3.6.1中,滿足!anyMatch表示所有手機號都不為“153”前綴,才得到true,這不就是noneMatch,主要看在項目當中如何靈活應用。
2.7、findFirst 和 findAny:返回流中第一個或任意一個元素
2.7.1、findFirst
findFirst用于返回流中第一個元素,如果流為空話,則返回一個空的Optional對象——
假設需要對一批同手機號的黑名單用戶按照時間戳降序排序,然后取出第一個即時間戳為最早的用戶,就可以使用findFirst——
List<People> peopleList = Arrays.asList( new People("王二","13178520000","20210409"), new People("李二","13178520000","20230401"), new People("張四","13178520000","20220509"), new People("趙六","13178520000","20220109") ); /** * 先按照時間升序排序,排序后的結果如下: * People{name='王二', tel='13178520000', time='20210409'} * People{name='趙六', tel='13178520000', time='20220109'} * People{name='張四', tel='13178520000', time='20220509'} * People{name='李二', tel='13178520000', time='20230401'} * *排序后,People{name='王二', tel='13178520000', time='20210409'}成了流中的第一個元素 */ People people = peopleList.stream().sorted(Comparator.comparing(People::getTime)).findFirst().orElse(null); System.out.println(people); //打印結果:People{name='王二', tel='13178520000', time='20210409'}2.7.2、findAny
findAny返回流中的任意一個元素,如果流為空,則通過Optional對象返回一個null。
假設有一個已經存在的黑名單手機號列表blackList,現(xiàn)在有一批新的手機號列表phoneNumber,需要基于blackList列表過濾出phoneNumber存在的黑名單手機號,最后從過濾出來的黑名單手機號當中挑選出來出來任意一個,即可以通過findAny實現(xiàn)——
//blackList是已經存在的黑名單列表 List<String> blackList = Arrays.asList("13378520000", "15138510000"); //新來的手機號列表 List<String> phoneNumber = Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000"); String blackPhone = phoneNumber.stream() //過濾出phoneNumber有包含在blackList的手機號,這類手機號即為黑名單手機號。 .filter(phone -> blackList.contains(phone)) //獲取過濾確定為黑名單手機號的任意一個 .findAny() //如果沒有則返回一個null .orElse(null); System.out.println(blackPhone); //打印結果:13378520000三、并行流
前面的案例主要都是以順序流來講解,接下來,就是講解Stream的并行流。在大數據量處理場景下,使用并行流可以提高某些操作效率,但同樣存在一些需要考慮的問題,并非所有情況下都可以使用。
3.1、什么是并行流:并行流的概念和原理
并行流是指通過將數據按照一定的方式劃分成多個片段分別在多個處理器上并行執(zhí)行,這就意味著,可能處理完成的數據順序與原先排序好的數據情況是不一致的。主要是用在比較大的數據量處理情況,若數據量太少,效率并不比順序流要高,因為底層其實就使用到了多線程的技術。
并行流的流程原理如下:
1、輸入數據:并行流的初始數據一般是集合或者數組,例如Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000");
2、劃分數據:將初始數據平均分成若干個子集,每個子集可以在不同的線程中獨立進行處理,這個過程通常叫“分支”(Forking),默認情況下,Java8并行流使用到了ForkJoinPool框架,會將Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000")劃分成更小的顆粒進行處理,可能會將該數組劃分成以下三個子集:
[13378520000, 13178520000] [1238510000, 13338510000] [13299920000]
3、處理數據:針對劃分好的子集并行進行相同的操作,例如包括過濾(filter)、映射(map)、去重(distinct)等,這個過程通常叫“計算”(Computing),例如需要過濾為前綴包括“133”的字符集合,那么,各個子集,就會處理得到以下結果:
[13378520000] [13338510000] []
4、合并結果:將所有子集處理完成的結果進行匯總,得到最終結果。這個過程通常叫“合并”(Merging),結果就會合并如下:
[13378520000,13338510000]
5、返回結果:返回最終結果。
通俗而言,就是順序流中,只有一個工人在摘水果,并行流中,是多個工人同時在摘水果。
3.2、創(chuàng)建并行流:通過 parallel() 方法將串行流轉換為并行流
可以通過parallel()方法將順序流轉換為并行流,操作很簡單,只需要在順序流上調用parallel()即可。
List<String> numbers = Arrays.asList("13378360000","13278240000","13178590000","13558120000"); //通過stream().parallel()方法創(chuàng)建一個并行流,使用map()方法將每個字符串轉換為截取前7位的字符,最后使用collect()方法將結果收集到一個新列表中 List<String> filNums = numbers.stream().parallel().map(s -> s.substring(0,7)).collect(Collectors.toList()); System.out.println(filNums); //打印結果:[1337836, 1327824, 1317859, 1355812]3.3、并行流的注意事項:并行流可能引發(fā)的線程安全,以及如何避免這些問題
在使用并發(fā)流的過程中,可能會引發(fā)以下線程安全問題:并行流中的每個子集都在不同線程運行,可能會導致對共享狀態(tài)的競爭和沖突。
避免線程問題的方法如下:避免修改共享狀態(tài),即在處理集合過程當中,避免被其他線程修改集合數據,可以使用鎖來保證線程安全。
使用無狀態(tài)操作:在并行流處理過程盡量使用無狀態(tài)操作,例如filter、map之類的,可以盡量避免線程安全和同步問題。
四、Optional
4.1、什么是 Optional:Optional 類型的作用和使用場景
在實際開發(fā)當中,Optional類型通常用于返回可能為空的方法、避免null值的傳遞和簡化復雜的判斷邏輯等場景。調用Optional對象的方法,需要通過isPresent()方法判斷值是否存在,如果存在則可以通過get()方法獲取其值,如果不存在則可以通過orElse()方法提供默認值,或者拋出自定義異常處理。
4.2、如何使用 Optional:如何使用 Optional 類型
使用Optional類型主要目的是在數據可能為空的情況下,提供一種更安全、更優(yōu)雅的處理方式。
以下是使用Optional類型的常用方法:
4.2.1、ofNullable()和isPresent()方法
將一個可能為null的對象包裝成Optional類型的對象,然后根據isPresent方法判斷對象是否包含空值——
String str = null; Optional<String> optStr = Optional.ofNullable(str); if (optStr.isPresent()){ System.out.println("Optional對象不為空"); }else { System.out.println("Optional對象為空"); } //打印結果:Optional對象為空4.2.2、get()方法
獲取Optional對象中的值,如果對象為空則拋出NoSuchElementException異常——
String str = null; Optional<String> optStr = Optional.ofNullable(str); if (optStr.isPresent()){ System.out.println("Optional對象不為空"); }else { System.out.println("Optional對象為空"); optStr.get(); }控制臺打印結果:
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at com.zhu.fte.biz.test.StreamTest.main(StreamTest.java:144)
Optional對象為空4.2.4、orElse()方法
獲取Optional對象中的值,如果對象為空則返回指定的默認值——
String str = null; Optional<String> optStr = Optional.ofNullable(str); if (optStr.isPresent()){ System.out.println("Optional對象不為空"); }else { System.out.println("Optional對象為空,返回默認值:" + optStr.orElse("null")); } //打印結果:Optional對象為空,返回默認值:null當然,如果不為空的話,則能正常獲取對象中的值——
String str = "測試"; Optional<String> optStr = Optional.ofNullable(str); if (optStr.isPresent()){ System.out.println("Optional對象不為空,返回值:" + optStr.orElse("null")); }else { System.out.println("Optional對象為空,返回默認值:" + optStr.orElse("null")); } //打印結果:Optional對象不為空,返回值:測試那么,問題來了,它是否能判斷“ ”這類空格的字符串呢,我實驗了一下,
String str = " "; Optional<String> optStr = Optional.ofNullable(str); if (optStr.isPresent()){ System.out.println("Optional對象不為空,返回值:" + optStr.orElse("null")); }else { System.out.println("Optional對象為空,返回默認值:" + optStr.orElse("null")); } //打印結果:Optional對象不為空,返回值:可見,這類空字符串,在orElse判斷當中,跟StringUtils.isEmpty()類似,都是把它當成非空字符串,但是StringUtils.isBlank()則判斷為空字符串。
4.2.5、orElseGet()方法
orElseGet()和orElse()類似,都可以提供一個默認值。兩者區(qū)別在于,orElse方法在每次調用時都會創(chuàng)建默認值,而orElseGet只在需要時才會創(chuàng)建默認值。
4.3、Optional 和 null 的區(qū)別: Optional 類型與 null 值的異同
兩者都可以表示缺失值的情況,兩者主要區(qū)別為:Optional類型是一種包裝器對象,可以將一個可能為空的對象包裝成一個Optional對象。這個對象可以通過調用
ofNullable()
、of()
或其他方法來創(chuàng)建。而null值則只是一個空引用,沒有任何實際的值。Optional類型還可以避免出現(xiàn)NullPointerException異常,具體代碼案例如下:
String str = null; //錯誤示范:直接調用str.length()方法會觸發(fā)NullPointerException //int length = str.length() //通過Optional類型避免NullPointerException Optional<String> optionalStr = Optional.ofNullable(str); if (optionalStr.isPresent()){//判斷Optional對象是否都包含非空值 int length = optionalStr.get().length(); System.out.println("字符串長度為:" + length); }else { System.out.println("字符串為空!"); } //使用map()方法對Optional對象進行轉換時,確保返回對結果不為null Optional<Integer> optionalLength = optionalStr.map(s -> s.length()); System.out.println("字符串長度為:" + optionalLength.orElse(-1)); // 使用orElse()方法提供默認值五、擴展流處理
除里以上常用的流處理之外,Java8還新增了一些專門用來處理基本類型的流,例如IntStream、LongStream、DoubleStream等,其對應的Api接口基本與前面案例相似,讀者可以自行研究。
最后,需要注意一點是,在流處理過程當中,盡量使用原始類型數據,避免裝箱操作,因為裝箱過程會有性能開銷、內存占用等問題,例如,當原始數據int類型被裝箱成Integer包裝類型時,這個過程會涉及到對象的創(chuàng)建、初始化、垃圾回收等過程,需要額外的性能開銷。
The above is the detailed content of How to use Stream streaming programming in Java8. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

There are three common methods to traverse Map in Java: 1. Use entrySet to obtain keys and values at the same time, which is suitable for most scenarios; 2. Use keySet or values to traverse keys or values respectively; 3. Use Java8's forEach to simplify the code structure. entrySet returns a Set set containing all key-value pairs, and each loop gets the Map.Entry object, suitable for frequent access to keys and values; if only keys or values are required, you can call keySet() or values() respectively, or you can get the value through map.get(key) when traversing the keys; Java 8 can use forEach((key,value)->

Optional can clearly express intentions and reduce code noise for null judgments. 1. Optional.ofNullable is a common way to deal with null objects. For example, when taking values ??from maps, orElse can be used to provide default values, so that the logic is clearer and concise; 2. Use chain calls maps to achieve nested values ??to safely avoid NPE, and automatically terminate if any link is null and return the default value; 3. Filter can be used for conditional filtering, and subsequent operations will continue to be performed only if the conditions are met, otherwise it will jump directly to orElse, which is suitable for lightweight business judgment; 4. It is not recommended to overuse Optional, such as basic types or simple logic, which will increase complexity, and some scenarios will directly return to nu.

The core workaround for encountering java.io.NotSerializableException is to ensure that all classes that need to be serialized implement the Serializable interface and check the serialization support of nested objects. 1. Add implementsSerializable to the main class; 2. Ensure that the corresponding classes of custom fields in the class also implement Serializable; 3. Use transient to mark fields that do not need to be serialized; 4. Check the non-serialized types in collections or nested objects; 5. Check which class does not implement the interface; 6. Consider replacement design for classes that cannot be modified, such as saving key data or using serializable intermediate structures; 7. Consider modifying

In Java, Comparable is used to define default sorting rules internally, and Comparator is used to define multiple sorting logic externally. 1.Comparable is an interface implemented by the class itself. It defines the natural order by rewriting the compareTo() method. It is suitable for classes with fixed and most commonly used sorting methods, such as String or Integer. 2. Comparator is an externally defined functional interface, implemented through the compare() method, suitable for situations where multiple sorting methods are required for the same class, the class source code cannot be modified, or the sorting logic is often changed. The difference between the two is that Comparable can only define a sorting logic and needs to modify the class itself, while Compar

To deal with character encoding problems in Java, the key is to clearly specify the encoding used at each step. 1. Always specify encoding when reading and writing text, use InputStreamReader and OutputStreamWriter and pass in an explicit character set to avoid relying on system default encoding. 2. Make sure both ends are consistent when processing strings on the network boundary, set the correct Content-Type header and explicitly specify the encoding with the library. 3. Use String.getBytes() and newString(byte[]) with caution, and always manually specify StandardCharsets.UTF_8 to avoid data corruption caused by platform differences. In short, by

Method reference is a way to simplify the writing of Lambda expressions in Java, making the code more concise. It is not a new syntax, but a shortcut to Lambda expressions introduced by Java 8, suitable for the context of functional interfaces. The core is to use existing methods directly as implementations of functional interfaces. For example, System.out::println is equivalent to s->System.out.println(s). There are four main forms of method reference: 1. Static method reference (ClassName::staticMethodName); 2. Instance method reference (binding to a specific object, instance::methodName); 3.

There are three common ways to parse JSON in Java: use Jackson, Gson, or org.json. 1. Jackson is suitable for most projects, with good performance and comprehensive functions, and supports conversion and annotation mapping between objects and JSON strings; 2. Gson is more suitable for Android projects or lightweight needs, and is simple to use but slightly inferior in handling complex structures and high-performance scenarios; 3.org.json is suitable for simple tasks or small scripts, and is not recommended for large projects because of its lack of flexibility and type safety. The choice should be decided based on actual needs.

How to quickly create new emails in Outlook is as follows: 1. The desktop version uses the shortcut key Ctrl Shift M to directly pop up a new email window; 2. The web version can create new emails in one-click by creating a bookmark containing JavaScript (such as javascript:document.querySelector("divrole='button'").click()); 3. Use browser plug-ins (such as Vimium, CrxMouseGestures) to trigger the "New Mail" button; 4. Windows users can also select "New Mail" by right-clicking the Outlook icon of the taskbar
