java8學習筆記目錄:
java8實戰讀書筆記:Lambda表達式語法與函數式編程接口
java8實戰讀書筆記:複合Lambda表達式
java8實戰讀書筆記:初識Stream、流的基本操做(流計算java
本節將詳細介紹Java8中的數值流、流的建立與Optional類的使用。編程
不知你們還記不得,在介紹函數式編程接口中爲了不基礎數據類型的裝箱/拆箱帶來的性能損耗,特地爲函數式接口引入了基礎數據類型的函數式編程接口,例如IntPredicate、LongPredicate、DoublePredicate。一樣,流API也考慮到基本數據類型的裝箱/拆箱會帶來性能損耗,引入了數值流,例如IntStream、LongStream、DoubleStream。數組
java8中提供了3個原始數據特化流,分別爲IntStream、LongStream、DoubleStream。本文將以IntStream進行講解,其餘流相似,只是數據類型分別表明Long或Double。微信
首先咱們仍是從一個示例開始本節的學習:計算菜單中全部菜品的卡路里之和。app
1public static void test_reduce_caluli(List<Dish> menu) { 2 int calories = menu.stream() 3 .map(Dish::getCalories) 4 .reduce(0, Integer::sum); 5 System.out.println("菜品中的總卡路里:" + calories); 6}
上面包含了一個基本數據類型的裝箱/拆箱動做,java8的流API提供了mapToInt方法,直接返回int類型的流運維
咱們先稍微看一下mapToInt的方法聲明:ide
1IntStream mapToInt(ToIntFunction<? super T> mapper) 2
接受一個T->int的函數式編程接口,直接返回IntStream流對象,並且IntStream自己提供了一些經常使用的聚合函數,例如sum。
使用IntStream來實現計算菜單中全部菜品的卡路里之和,其示例以下:函數式編程
1public static void test_reduce_caluli_intStream(List<Dish> menu) { 2 int calories = menu.stream() 3 .mapToInt(Dish::getCalories) 4 .sum(); 5 System.out.println("菜品中的總卡路里:" + calories); 6 7}
使用了特化流例如IntStream後,將不能再自動轉換爲其對應的封裝對象流Stream< T >了,咱們能夠隨意從IntStream對象中對應的通用方法的函數聲明,例如IntStream#map函數的聲明以下:函數
1IntStream map(IntUnaryOperator mapper);
只能接受int -> int的函數式編程接口,若是想將IntStream轉回到Stream< Integer >,該如何處理呢?性能
IntStream提供了boxed()方法來實現將基礎數據類型轉換回對應的包裝類型的流。
Stream中定義的方法,IntStream也能夠使用,例如map、flatMap、distinict等,IntStream除這些以外,還提供了經常使用的聚合函數,例如sum、min、max、average(平均數)。
1OptionalDouble average(); 2OptionalInt max(); 3OptionalInt min(); 4int sum();
有關Optional相關的類將在下文詳細介紹。
另外除了上面提到的聚合函數,IntStream還提供了兩個與數值範圍的方法:
1public static IntStream range(int startInclusive, int endExclusive); 2public static IntStream rangeClosed(int startInclusive, int endExclusive);
rangeClosed與range的區別就是rangeClosed包含結束邊界,舉一個簡單示例以下:
1public static void test_range() { 2 long count = IntStream.range(1,100) 3 .filter( i -> i % 2 == 0 ) 4 .count(); 5 System.out.println("count:" + count); 6 7}
計算【1,100)中包含的偶數個數,將輸出49。若是將range(1,100)修改成rangeClosed(1,100),在輸出的個數爲50。
java 8的Stream提供了兩個重載的of函數來顯示的構建流,其聲明以下:
1public static<T> Stream<T> of(T t) 2public static<T> Stream<T> of(T... values) 3
2.2 經過數組構建流
經過Arrays.stream構建流,其聲明以下:
Arrays#stream
1public static <T> Stream<T> stream(T[] array) 2
能夠經過文件流建立流,在java.nio.file.Files類中定義了以下建立流的方法。
1public static Stream<Path> list(Path dir) throws IOException 2public static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options) 3public static Stream<Path> walk(Path start, FileVisitOption... options) 4public static Stream<Path> find(Path start, int maxDepth,BiPredicate<Path, BasicFileAttributes> matcher, 5 FileVisitOption... options) 6public static Stream<String> lines(Path path, Charset cs) 7public static Stream<String> lines(Path path) throws IOException 8
下面咱們舉一個示例:找出一個文件中不一樣詞的個數。
1public static void test_file_stram() { 2 long uniqueWords = 0; 3 try(Stream<String> lines = Files.lines(Paths.get("d:/tmp/words.txt"), Charset.defaultCharset())) { 4 uniqueWords = lines.flatMap(line -> Arrays.stream(line.split("" ))) 5 .distinct() 6 .count(); 7 8 System.out.println("不重複字符個數:" + uniqueWords); 9 } catch (IOException e) { 10 e.printStackTrace(); 11 } 12}
Stream API提供了兩個靜態方法從函數生成流:iterate、generate,咱們先來看一下其函數聲明:
1public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) 2public static<T> Stream<T> generate(Supplier<T> s) 3
iterate方法的第一個參數類型爲T,表示其初始值,第二個參數以下:
即其函數式聲明爲爲T-T。其示例以下:
1public static void test_iterate() { 2 Stream.iterate(0, a -> a + 2) 3 .limit(10) 4 .forEach(System.out::println); 5}
注意:因爲是無限流,故千萬記得使用limit截斷流,不然會無限循環下去。
其參數爲Supplier< T >,其定義以下:
即構造一個T類型的對象,舉例以下:
1public static void test_iterate() { 2 Stream.iterate(0, a -> a + 2) 3 .limit(10) 4 .forEach(System.out::println); 5}
這個在前面的示例中用的最多,就不作過多介紹。
爲了更優雅的處理null值,避免空指針錯誤,java8中引入Optional類。
接下來對這些方法一一作個介紹。
public static< T> Optional< T> empty()
建立一個Optional對象,其內部持有的對象爲null。
public static < T > Optional< T > of(T value)
使用value的值,建立一個Optional對象。
public static < T > Optional< T > ofNullable(T value)
使用v去建立一個Optional對象,若是value爲null,則返回empty()。
public T get()
從Optional對象獲取內嵌的對象,若是爲空,則拋出NoSuchElementException。
public boolean isPresent()
判斷Optional對象中包含的值是否存在。
public void ifPresent(Consumer consumer)
若是Optional包裹的對象存在,則消費該對象。Consumer的函數式編程接口:T -> void。
public Optional< T > filter(Predicate predicate)
若是Optional中包裹的對象爲空,則返回自身,不然若是包裹的對象知足predicate表達式,則返回自身,不然返回empty()。
public< U > Optional< U > map(Function mapper)
若是Optional對象中包裹的對象爲空,則返回empty(),不然運用(T-U),包裹U,固然若是U爲空,則返回empty()。
public< U > Optional< U > flatMap(Function> mapper)
若是Option對象中包裹的對象爲空,則返回empty(),不然使用對Optional中的包裹的對象value應用Function,最終返回Optional對象。
public T orElse(T other)
返回Optional中包裹的對象,若是其值爲空,則返回other。
public T orElseGet(Supplier other)
返回Optional中包裹的對象,若是其值爲空,則返回 Supplier函數式編輯接口中建立的值。
其示例代碼以下:
1public static void test_option() { 2Map<String, String> data = new HashMap<>(); 3Optional<String> value = Optional.ofNullable(data.get("userName")); 4// 若是存在userName值,則輸出 5value.ifPresent(System.out::println); 6}
本文就介紹到這裏了,本文詳細介紹了java8中的數值流、Stream的建立以及java8中Optional類的使用。
查看更多文章請關注微信公衆號:
一波廣告來襲,做者新書《RocketMQ技術內幕》已出版上市:
《RocketMQ技術內幕》已出版上市,目前可在主流購物平臺(京東、天貓等)購買,本書從源碼角度深度分析了RocketMQ NameServer、消息發送、消息存儲、消息消費、消息過濾、主從同步HA、事務消息;在實戰篇重點介紹了RocketMQ運維管理界面與當前支持的39個運維命令;並在附錄部分羅列了RocketMQ幾乎全部的配置參數。本書獲得了RocketMQ創始人、阿里巴巴Messaging開源技術負責人、Linux OpenMessaging 主席的高度承認並做序推薦。目前是國內第一本成體系剖析RocketMQ的書籍。