Java8之Stream/Map

本篇用代碼示例結合JDk源碼講了Java8引入的工具接口Stream以及新Map接口提供的經常使用默認方法.
    參考:http://winterbe.com/posts/2014/03/16/java-8-tutorial/

    1.Stream示例java

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();
    }
}
相關文章
相關標籤/搜索