java_Stream流和Optional
這里寫目錄標題
- 1.Stream流
- 入門案例:
- 創建流
- 中間操作
- 終結操作
- forEach
- count
- max&min
- collect
- 查找與匹配
- anyMatch
- allMatch
- noneMatch
- findAny
- findFirst
- reduce歸并
- 注意事項
- 2.Optional
- 使用
- 創建
- 安全消費值
- 獲取值
- 安全獲取值
- 過濾
- 判斷
- 數據轉換
1.Stream流
? Java8的Stream使用的是函數式編程模式,如同它的名字一樣,它可以被用來對集合或數組進行鏈狀流式的操作??梢愿奖愕淖屛覀儗匣驍到M操作。
入門案例:
public class StreamDemo {public static void main(String[] args) {// System.out.println(getAuthors());List<Author> authors = getAuthors();authors.stream().distinct().filter(author -> author.getAge()<18).forEach(author -> System.out.println(author.getName())); //必須要有終結操作}idea 對debug對stream流的直觀體現:
創建流
單列集合: 集合對象.stream()
數組:Arrays.stream(數組) 或者使用Stream.of(數組)來創建
雙列集合:轉換成單列集合后再創建
Map<String,Integer> map = new HashMap<>();map.put("蠟筆小新",19);map.put("黑子",17);map.put("日向翔陽",16);Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();中間操作
-
filter 流中的元素進行條件過濾
-
map 可以把對流中的元素進行計算或轉換。
這個Map是映射的意思,映射成其他的元素
- distinct 可以去除流中的重復元素。
distinct方法是依賴Object的equals方法來判斷是否是相同對象的。所以需要注意重寫equals方法
-
sorted 可以對流中的元素進行排序。
對象需要實現comparable接口 或者 傳一個Comparator的匿名內部類
- limit
可以設置流的最大長度,超出的部分將被拋棄。 limit 獲取前N個元素
public void t3(){getAuthors().stream()//年齡最小的前2個.sorted((o1, o2) -> o1.getAge() - o2.getAge()).limit(2).forEach(author -> System.out.println(author.getName()+author.getAge())); }- skip 跳過流中的前n個元素,返回剩下的元素
-
flatMap
map只能把一個對象轉換成另一個對象來作為流中的元素。而flatMap可以把一個對象轉換成多個對象作為流中的元素。
public void t5(){getAuthors().stream().flatMap(new Function<Author, Stream<?>>() {@Overridepublic Stream<?> apply(Author author) {return author.getBooks().stream();}}).forEach(book -> System.out.println(book)); }public void t5(){getAuthors().stream().flatMap(author -> author.getBooks().stream()).forEach(book -> System.out.println(book));}
終結操作
forEach
? 對流中的元素進行遍歷操作,我們通過傳入的參數去指定對遍歷到的元素進行什么具體操作。
count
? 可以用來獲取當前流中元素的個數。
@Test public void t7(){List<Author> authors = getAuthors();long count = authors.stream().distinct().count();System.out.println(count); }max&min
? 可以用來或者流中的最值。
public void t7(){// 分別獲取這些作家的所出書籍的最高分和最低分并打印。List<Author> authors = getAuthors();Optional<Integer> max = authors.stream().flatMap(author -> author.getBooks().stream()).map(book -> book.getScore()).max((o1, o2) -> o1 - o2);Integer integer = max.get();System.out.println(integer);}collect
? 把當前流轉換成一個集合。
List<Author> authors = getAuthors(); List<String> list = authors.stream().map(author -> author.getName()).collect(Collectors.toList()); //使用 java.util.stream.Collectors; 這個工具類 System.out.println(list);// set 一模一樣
map 需要注意的是 key 數據一定不能重復(下面用了distinct 其實是對author對象euaqls 去重,也沒實際是對name去重)
List<Author> authors = getAuthors(); Map<String, List<Book>> map = authors.stream().distinct().collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));System.out.println(map);查找與匹配
anyMatch
? 可以用來判斷是否有任意符合匹配條件的元素,結果為boolean類型。
allMatch
? 可以用來判斷是否都符合匹配條件,結果為boolean類型。如果都符合結果為true,否則結果為false。
public void t10() {List<Author> authors = getAuthors();boolean b = authors.stream().anyMatch(author -> author.getAge() > 18);System.out.println(b); }public void t10() {List<Author> authors = getAuthors();boolean b = authors.stream().allMatch(author -> author.getAge() > 18);System.out.println(b);}noneMatch
? 可以判斷流中的元素是否都不符合匹配條件。如果都不符合結果為true,否則結果為false
// 判斷作家是否都沒有超過100歲的。List<Author> authors = getAuthors();boolean b = authors.stream().noneMatch(author -> author.getAge() > 100);System.out.println(b); //true 就是都沒有findAny
獲取流中的任意一個元素。該方法沒有辦法保證獲取的一定是流中的第一個元素。例子:
獲取任意一個年齡大于18的作家,如果存在就輸出他的名字 List<Author> authors = getAuthors();Optional<Author> optionalAuthor = authors.stream().filter(author -> author.getAge()>18).findAny();optionalAuthor.ifPresent(author -> System.out.println(author.getName()));findFirst
獲取流中的第一個元素。例子:
獲取一個年齡最小的作家,并輸出他的姓名。 // 獲取一個年齡最小的作家,并輸出他的姓名。List<Author> authors = getAuthors();Optional<Author> first = authors.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).findFirst();first.ifPresent(author -> System.out.println(author.getName()));它倆區別的注意點~~
返回的元素是不確定的,對于同一個列表多次調用findAny()有可能會返回不同的值。使用findAny()是為了更高效的性能。如果是數據較少,串行地情況下,一般會返回第一個結果,如果是并行的情況,那就不能確保是第一個。
所以 你測試的時候可能發現 他倆結果一樣 可能是因為數據量小
reduce歸并
內部操作:
發現 返回的值 是和元素的類型一樣的 所以一般先進行map操作 連起來叫 map-reduce操作
@Testpublic void t12() { // 年齡和 List<Author> authors = getAuthors();Integer sum = authors.stream().map(author -> author.getAge()).reduce(0, new BinaryOperator<Integer>() {@Overridepublic Integer apply(Integer re, Integer elem) {return re + elem;}});System.out.println(sum);}min 和 max 操作其實就是reduce
使用reduce求所有作者中年齡的最大值 // 使用reduce求所有作者中年齡的最大值List<Author> authors = getAuthors();Integer max = authors.stream().map(author -> author.getAge()).reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);System.out.println(max); 使用reduce求所有作者中年齡的最小值 // 使用reduce求所有作者中年齡的最小值List<Author> authors = getAuthors();Integer min = authors.stream().map(author -> author.getAge()).reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);System.out.println(min);注意事項
- 惰性求值(如果沒有終結操作,沒有中間操作是不會得到執行的)
- 流是一次性的(一旦一個流對象經過一個終結操作后。這個流就不能再被使用)
- 不會影響原數據(我們在流中可以多數據做很多處理。但是正常情況下是不會影響原來集合中的元素的。這往往也是我們期望的)
特殊:影響原數據
List<Author> authors = getAuthors(); authors.stream().forEach(author -> System.out.println("原數據"+author.getAge()));authors.stream().map(new Function<Author, Author>() {@Overridepublic Author apply(Author author) {author.setAge(author.getAge() + 10);return author;}}).forEach(author -> System.out.println(author.getAge()));authors.stream().forEach(author -> System.out.println("原數據"+author.getAge())); //原數據都改變了 原數據33 原數據15 原數據14 原數據14 43 25 24 24 原數據43 原數據25 原數據24 原數據24Process finished with exit code 0 @Testpublic void t13() {List<Author> authors = getAuthors();authors.stream().forEach(author -> System.out.println("原數據"+author.getAge()));authors.stream().map( author -> author.getAge()+10).forEach(age -> System.out.println(age));authors.stream().forEach(author -> System.out.println("原數據"+author.getAge()));} 原數據33 原數據15 原數據14 原數據14 43 25 24 24 原數據33 原數據15 原數據14 原數據14Process finished with exit code 0也就是不會影響集合內元素項的類型、個數、順序。但由于值傳遞,可能改變元素項的屬性
2.Optional
在JDK8中引入了Optional,養成使用Optional的習慣后你可以寫出更優雅的代碼來避免空指針異常。(比如mapper 查某個數據)
在實際開發中我們的數據很多是從數據庫獲取的。Mybatis從3.5版本可以也已經支持Optional了。我們可以直接把dao方法的返回值類型定義成Optional類型,MyBastis會自己把數據封裝成Optional對象返回。封裝的過程也不需要我們自己操作。
并且在很多函數式編程相關的API中也都用到了Optional。
使用
創建
Optional就好像是包裝類,可以把我們的具體數據封裝Optional對象內部。然后我們去使用Optional中封裝好的方法操作封裝進去的數據就可以非常優雅的避免空指針異常。
@Testpublic void t1() {Optional<Author> authorOptional = getAuthorOptional();authorOptional.ifPresent(author -> System.out.println(author)); //如果不是null (ifPresent)才去消費}private Optional<Author> getAuthorOptional() {Author author = new Author(3L, "易", 14, "是這個世界在限制他的思維", null);return Optional.ofNullable(author);//ofNullable創建}?
- 不經常用的兩個創建方法:
如果你確定一個對象不是空的則可以使用Optional的靜態方法of來把數據封裝成Optional對象。
Author author = new Author();Optional<Author> authorOptional = Optional.of(author);? 但是一定要注意,如果使用of的時候傳入的參數必須不為null。(嘗試下傳入null會出現什么結果)
? 如果一個方法的返回值類型是Optional類型。而如果我們經判斷發現某次計算得到的返回值為null,這個時候就需要把null封裝成Optional對象返回。這時則可以使用Optional的靜態方法empty來進行封裝。
Optional.empty()- 更多使用 更方便的應該是ofNullable
安全消費值
? 我們獲取到一個Optional對象后肯定需要對其中的數據進行使用。這時候我們可以使用其ifPresent方法對來消費其中的值。
? 這個方法會判斷其內封裝的數據是否為空,不為空時才會執行具體的消費代碼。這樣使用起來就更加安全了。
? 例如,以下寫法就優雅的避免了空指針異常。
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.ifPresent(author -> System.out.println(author.getName()));獲取值
? 如果我們想獲取值自己進行處理可以使用get方法獲取,但是不推薦。因為當Optional內部的數據為空的時候會出現異常。
@Test public void t2() {Optional<Author> authorOptional = getAuthorOptional();Author author = authorOptional.get(); //如果下面 return Optional.ofNullable(null); 那么這一行會報錯System.out.println(author);//Author(id=3, name=易, age=14, intro=是這個世界在限制他的思維, books=null) }private Optional<Author> getAuthorOptional() {Author author = new Author(3L, "易", 14, "是這個世界在限制他的思維", null);return Optional.ofNullable(author); }安全獲取值
? 如果我們期望安全的獲取值。我們不推薦使用get方法,而是使用Optional提供的以下方法。
-
orElseGet
獲取數據并且設置數據為空時的默認值。如果數據不為空就能獲取到該數據。如果為空則根據你傳入的參數來創建對象作為默認值返回。
public void t2() {Optional<Author> authorOptional = getAuthorOptional();Author author = authorOptional.orElseGet(() -> new Author());//否則獲取System.out.println(author);//Author(id=null, name=null, age=null, intro=null, books=null)}private Optional<Author> getAuthorOptional() {Author author = new Author(3L, "易", 14, "是這個世界在限制他的思維", null);return Optional.ofNullable(null);}
-
orElseThrow
獲取數據,如果數據不為空就能獲取到該數據。如果為空則根據你傳入的參數來創建異常拋出。
@Testpublic void t3() {Optional<Author> authorOptional = getAuthorOptional();Author author = null;try {author = authorOptional.orElseThrow(() -> new RuntimeException("數據為空"));} catch (Throwable e) {e.printStackTrace();}System.out.println(author);}private Optional<Author> getAuthorOptional() {Author author = new Author(3L, "易", 14, "是這個世界在限制他的思維", null);return Optional.ofNullable(null);}
過濾
? 我們可以使用filter方法對數據進行過濾。如果原本是有數據的,但是不符合判斷,也會變成一個無數據的Optional對象。
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));判斷
我們可以使用isPresent方法進行是否存在數據的判斷。如果為空返回值為false,如果不為空,返回值為true。但是這種方式并不能體現Optional的好處,更推薦使用ifPresent方法。
Optional<Author> authorOptional = Optional.ofNullable(getAuthor());if (authorOptional.isPresent()) {System.out.println(authorOptional.get().getName());}數據轉換
? Optional還提供了map可以讓我們的對數據進行轉換,并且轉換得到的數據也還是被Optional包裝好的,保證了我們的使用安全。
例如我們想獲取作家的書籍集合。
public void t4() {Optional<Author> authorOptional = getAuthorOptional();Optional<List<Book>> books = authorOptional.map(author -> author.getBooks());books.ifPresent(books1 -> System.out.println(books1));//[Book(id=1, name=刀的兩側是光明與黑暗, category=哲學,愛情, score=88, intro=用一把刀劃分了愛恨), Book(id=2, name=一個人不能死在同一把刀下, category=個人成長,愛情, score=99, intro=講述如何從失敗中明悟真理)]}private Optional<Author> getAuthorOptional() {Author author = new Author(3L, "易", 14, "是這個世界在限制他的思維", null);List<Book> books1 = new ArrayList<>();books1.add(new Book(1L, "刀的兩側是光明與黑暗", "哲學,愛情", 88, "用一把刀劃分了愛恨"));books1.add(new Book(2L, "一個人不能死在同一把刀下", "個人成長,愛情", 99, "講述如何從失敗中明悟真理"));author.setBooks(books1);return Optional.ofNullable(author);}總結
以上是生活随笔為你收集整理的java_Stream流和Optional的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win8下cocos2dx-3.2+VS
- 下一篇: PDF权限密码怎么解除