Java8之Stream/Map
阿新 • • 發佈:2018-12-01
本篇用程式碼示例結合JDk原始碼講了Java8引入的工具介面Stream以及新Map介面提供的常用預設方法.
參考:http://winterbe.com/posts/2014/03/16/java-8-tutorial/
1.Stream示例
package com.mavsplus.java8.turtorial.streams; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; /** * java.util.Stream使用例子 * * <pre> * java.util.Stream表示了某一種元素的序列,在這些元素上可以進行各種操作。Stream操作可以是中間操作,也可以是完結操作。 * 完結操作會返回一個某種型別的值,而中間操作會返回流物件本身,並且你可以通過多次呼叫同一個流操作方法來將操作結果串起來。 * Stream是在一個源的基礎上創建出來的,例如java.util.Collection中的list或者set(map不能作為Stream的源)。 * Stream操作往往可以通過順序或者並行兩種方式來執行。 * </pre> * * public interface Stream<T> extends BaseStream<T, Stream<T>> { * <p> * 可以看到Stream是一個介面,其是1.8引入 * * <p> * Java 8中的Collections類的功能已經有所增強,你可以之直接通過呼叫Collections.stream()或者Collection. * parallelStream()方法來建立一個流物件 * * @author landon * @since 1.8.0_25 */ public class StreamUtilExample { private List<String> stringList = new ArrayList<>(); public StreamUtilExample() { init(); } private void init() { initStringList(); } /** * 初始化字串列表 */ private void initStringList() { stringList.add("zzz1"); stringList.add("aaa2"); stringList.add("bbb2"); stringList.add("fff1"); stringList.add("fff2"); stringList.add("aaa1"); stringList.add("bbb1"); stringList.add("zzz2"); } /** * Filter接受一個predicate介面型別的變數,並將所有流物件中的元素進行過濾。該操作是一箇中間操作, * 因此它允許我們在返回結果的基礎上再進行其他的流操作 * (forEach)。ForEach接受一個function介面型別的變數,用來執行對每一個元素的操作 * 。ForEach是一箇中止操作。它不返回流,所以我們不能再呼叫其他的流操作 */ public void useStreamFilter() { // stream()方法是Collection介面的一個預設方法 // Stream<T> filter(Predicate<? super T> // predicate);filter方法引數是一個Predicate函式式介面並繼續返回Stream介面 // void forEach(Consumer<? super T> action);foreach方法引數是一個Consumer函式式介面 // 解釋:從字串序列中過濾出以字元a開頭的字串並迭代列印輸出 stringList.stream().filter((s) -> s.startsWith("a")).forEach(System.out::println); } /** * Sorted是一箇中間操作,能夠返回一個排過序的流物件的檢視。流物件中的元素會預設按照自然順序進行排序, * 除非你自己指定一個Comparator介面來改變排序規則. * * <p> * 一定要記住,sorted只是建立一個流物件排序的檢視,而不會改變原來集合中元素的順序。原來string集合中的元素順序是沒有改變的 */ public void useStreamSort() { // Stream<T> sorted();返回Stream介面 // 另外還有一個 Stream<T> sorted(Comparator<? super T> // comparator);帶Comparator介面的引數 stringList.stream().sorted().filter((s) -> s.startsWith("a")).forEach(System.out::println); // 輸出原始集合元素,sorted只是建立排序檢視,不影響原來集合順序 stringList.stream().forEach(System.out::println); } /** * map是一個對於流物件的中間操作,通過給定的方法,它能夠把流物件中的每一個元素對應到另外一個物件上。 * 下面的例子就演示瞭如何把每個string都轉換成大寫的string. * 不但如此,你還可以把每一種物件對映成為其他型別。對於帶泛型結果的流物件,具體的型別還要由傳遞給map的泛型方法來決定。 */ public void useStreamMap() { // <R> Stream<R> map(Function<? super T, ? extends R> mapper); // map方法引數為Function函式式介面(R_String,T_String). // 解釋:將集合元素轉為大寫(每個元素對映到大寫)->降序排序->迭代輸出 // 不影響原來集合 stringList.stream().map(String::toUpperCase).sorted((a, b) -> b.compareTo(a)).forEach(System.out::println); } /** * 匹配操作有多種不同的型別,都是用來判斷某一種規則是否與流物件相互吻合的。所有的匹配操作都是終結操作,只返回一個boolean型別的結果 */ public void useStreamMatch() { // boolean anyMatch(Predicate<? super T> predicate);引數為Predicate函式式介面 // 解釋:集合中是否有任一元素匹配以'a'開頭 boolean anyStartsWithA = stringList.stream().anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // boolean allMatch(Predicate<? super T> predicate); // 解釋:集合中是否所有元素匹配以'a'開頭 boolean allStartsWithA = stringList.stream().allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // boolean noneMatch(Predicate<? super T> predicate); // 解釋:集合中是否沒有元素匹配以'd'開頭 boolean nonStartsWithD = stringList.stream().noneMatch((s) -> s.startsWith("d")); System.out.println(nonStartsWithD); } /** * Count是一個終結操作,它的作用是返回一個數值,用來標識當前流物件中包含的元素數量 */ public void useStreamCount() { // long count(); // 解釋:返回集合中以'a'開頭元素的數目 long startsWithACount = stringList.stream().filter((s) -> s.startsWith("a")).count(); System.out.println(startsWithACount); System.out.println(stringList.stream().count()); } /** * 該操作是一個終結操作,它能夠通過某一個方法,對元素進行削減操作。該操作的結果會放在一個Optional變數裡返回。 */ public void useStreamReduce() { // Optional<T> reduce(BinaryOperator<T> accumulator); // @FunctionalInterface public interface BinaryOperator<T> extends // BiFunction<T,T,T> { // @FunctionalInterface public interface BiFunction<T, U, R> { R apply(T // t, U u); Optional<String> reduced = stringList.stream().sorted().reduce((s1, s2) -> s1 + "#" + s2); // 解釋:集合元素排序後->reduce(削減 )->將元素以#連線->生成Optional物件(其get方法返回#拼接後的值) reduced.ifPresent(System.out::println); System.out.println(reduced.get()); } /** * 使用並行流 * <p> * 流操作可以是順序的,也可以是並行的。順序操作通過單執行緒執行,而並行操作則通過多執行緒執行. 可使用並行流進行操作來提高執行效率 */ public void useParallelStreams() { // 初始化一個字串集合 int max = 1000000; List<String> values = new ArrayList<>(); for (int i = 0; i < max; i++) { UUID uuid = UUID.randomUUID(); values.add(uuid.toString()); } // 使用順序流排序 long sequenceT0 = System.nanoTime(); values.stream().sorted(); long sequenceT1 = System.nanoTime(); // 輸出:sequential sort took: 51921 ms. System.out.format("sequential sort took: %d ms.", sequenceT1 - sequenceT0).println(); // 使用並行流排序 long parallelT0 = System.nanoTime(); // default Stream<E> parallelStream() { // parallelStream為Collection介面的一個預設方法 values.parallelStream().sorted(); long parallelT1 = System.nanoTime(); // 輸出:parallel sort took: 21432 ms. System.out.format("parallel sort took: %d ms.", parallelT1 - parallelT0).println(); // 從輸出可以看出:並行排序快了一倍多 } public static void main(String[] args) { StreamUtilExample example = new StreamUtilExample(); example.useStreamFilter(); example.useStreamMap(); example.useStreamMatch(); example.useStreamCount(); example.useStreamReduce(); example.useParallelStreams(); } }
2.Map介面中新的預設方法示例
package com.mavsplus.java8.turtorial.streams; import java.util.HashMap; import java.util.Map; /** * map是不支援流操作的。而更新後的map現在則支援多種實用的新方法,來完成常規的任務 * * @author landon * @since 1.8.0_25 */ public class MapUtilExample { private Map<Integer, String> map = new HashMap<>(); public MapUtilExample() { initPut(); } /** * 使用更新後的map進行putIfAbsent */ private void initPut() { // putIfAbsent為Map介面中新增的一個預設方法 /** * <code> default V putIfAbsent(K key, V value) { V v = get(key); if (v == null) { v = put(key, value); } return v; } </code> */ // 如果map中有對應K對映的V且不為null則直接返回;否則執行put for (int i = 0; i < 10; i++) { map.putIfAbsent(i, "value" + i); } // 放入了一個null元素 map.putIfAbsent(10, null); // 替換null map.putIfAbsent(10, "value10"); // 因為K-10有對映且不為null則忽略V-value11 map.putIfAbsent(10, "value11"); } /** * 使用更新後的map進行for-each */ public void forEach() { // default void forEach(BiConsumer<? super K, ? super V> action) // Map介面中新增的預設方法 // @FunctionalInterface public interface BiConsumer<T, U> {void accept(T // t, U u); map.forEach((id, val) -> System.out.println(val)); } /** * 使用更新後的map進行compute——->重對映 */ public void compute() { // default V computeIfPresent(K key,BiFunction<? super K, ? super V, ? // extends V> remappingFunction) // Map介面中新增的預設方法 // @FunctionalInterface public interface BiFunction<T, U, R> {R apply(T // t, U u); // --> V apply(K k,V v) // ifPresent會判斷key對應的v是否是null,不會null才會compute->否則直接返回null // 解釋:將K-3對映的value->compute->"value3" + 3 = value33 map.computeIfPresent(3, (key, val) -> val + key); System.out.println(map.get(3)); // 解釋:這裡將K-3對映的value進行重對映->null // 該方法原始碼實現會判斷如果newValue為null則會執行remove(key)方法,將移除key map.computeIfPresent(9, (key, val) -> null); // 從上面的解釋中得到,輸出為false,因為已經被移除了 System.out.println(map.containsKey(9)); // default V computeIfAbsent(K key,Function<? super K, ? extends V> // mappingFunction) // 解釋:程式碼實現上看,如果K-15對映的值為null,即不存在或者為null,則執行對映->所以本例來看(沒有15的key),該方法相當於插入一個新值 map.computeIfAbsent(15, (key) -> "val" + key); System.out.println(map.containsKey(15)); // 因為K-4對映的值存在,所以直接返回,即不會重對映,所以輸出依然會是value4 map.computeIfAbsent(4, key -> "bam"); System.out.println(map.get(4)); } /** * 使用更新後的map進行remove */ public void remove() { // default boolean remove(Object key, Object value) { // Map介面中新增的預設方法 // 其原始碼實現是 // 1.當前key對應的值和傳入的引數不一致時則直接返回,移除失敗(用的是Objects.equals方法) // 2.當前key對應的值為null且!containsKey(key),移除失敗(即當前map中根本不存在這個key_【因為有一種情況是有這個key但是key對映的值為null】) // ->否則執行移除 /** * <code> * default boolean remove(Object key, Object value) { Object curValue = get(key); if (!Objects.equals(curValue, value) || (curValue == null && !containsKey(key))) { return false; } remove(key); return true; } * </code> */ map.remove(3, "value4"); System.out.println(map.get(3)); // key和v匹配時則移除成功 map.remove(3, "value33"); System.out.println(map.get(3)); } /** * getOrDefault是一個有用的方法 */ public void getOrDefault() { // default V getOrDefault(Object key, V defaultValue) { // Map介面中新增的預設方法 /** * <code> * default V getOrDefault(Object key, V defaultValue) { V v; return (((v = get(key)) != null) || containsKey(key)) ? v : defaultValue; } * </code> */ // 原始碼實現: // 1.如果對應的key有value且不為null,則直接返回value;如果為null且包含該key,則返回null(總之即必須要有該key) // 2.如果沒有該key,則用預設值 String retV = map.getOrDefault("20", "not found"); System.out.println(retV); // 加入一個null map.putIfAbsent(30, null); // 輸出null System.out.println(map.get(30)); // 輸出null System.out.println(map.getOrDefault(30, "value30")); } /** * 合併 */ public void merge() { // default V merge(K key, V value,BiFunction<? super V, ? super V, ? // extends V> remappingFunction) // @FunctionalInterface public interface BiFunction<T, U, R> { R apply(T // t, U u); // merge為Map介面新增的預設方法 /** * <code> default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); V oldValue = get(key); V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); if(newValue == null) { remove(key); } else { put(key, newValue); } return newValue; } * </code> */ // 其原始碼實現: // 1.分別檢查引數remappingFunction和value是否為null(呼叫Objects.requireNonNull).->為null則丟擲空指標 // 2.判斷oldValue是否為null,如果為null則將傳入的newValue賦值;如果oldValue不為null則執行merge函式 // --->apply(oldValue, value) // 3.判斷newValue->如果為null則執行移除;否則執行插入 // k-9的值在執行compute方法的時候已經被移除了->所以oldValue為null->所以newValue為傳入的引數value9->執行插入 // 所以這裡輸出為value9 String newValue1 = map.merge(9, "value9", (value, newValue) -> value.concat(newValue)); System.out.println(newValue1); System.out.println(map.get(9)); // k-9的值現在已經為value9了,所以執行merge函式->"value9".concat("concat")->newValue為"value9concat" // 執行插入,所以這裡輸出為value9concat String newValue2 = map.merge(9, "concat", (value, newValue) -> value.concat(newValue)); System.out.println(newValue2); System.out.println(map.get(9)); // k-8值存在為value8->執行merge函式->直接返回"NewMerge8"->newValue為"NewMerge8" // 執行put->所以這裡輸出"NewMerge8" map.merge(8, "merge", (value, newValue) -> "NewMerge8"); System.out.println(map.get(8)); } public static void main(String[] args) { MapUtilExample example = new MapUtilExample(); example.forEach(); example.compute(); example.remove(); example.getOrDefault(); example.merge(); } }