Java函數(shù)式編程(二)

一、迭代

public static void main(String[] args) {
        int[] nums = {1, 2, 3};
        //外部迭代
        int sum = 0;
        for (int i: nums){
            sum += i;
        }

        //內(nèi)部迭代
        int sum2 = IntStream.of(nums).sum();

        System.out.println(sum == sum2);
}
  1. 中間操作:返回Stream
  2. 終止操作:返回最終結(jié)果

惰性求值:若沒有執(zhí)行終止操作,則中間操作不會執(zhí)行兰绣。
流只能遍歷一次弦追。

二、流的創(chuàng)建

常見的Stream流創(chuàng)建.png
public class Test {
    public static void main(String[] args) {

        //從集合創(chuàng)建
        List list = new ArrayList();
        list.stream();

        //從數(shù)組創(chuàng)建
        Arrays.stream(new int[]{1, 2, 3});

        //創(chuàng)建數(shù)字流
        IntStream.of(1, 2, 3);
        IntStream.rangeClosed(1, 10);

        //使用Random創(chuàng)建無限流
        new Random().ints().limit(10);

        //自己創(chuàng)建
        Random random = new Random();
        Stream.generate(() -> random.nextInt(10)).limit(10);

        try(Stream<String> lines = Files.lines(Paths.get(""))){
            //由文件生成流
        }catch (IOException e){
            //顯式捕獲流中的異常
        }
    }
  
}

對象流和數(shù)值流的轉(zhuǎn)化:

  1. 映射到數(shù)值流:mapToInt、mapToDouble寝蹈、mapToLong返回一個數(shù)值流IntStream慧域、DoubleStream榆骚、LongStream片拍。
  2. 轉(zhuǎn)換回對象流:使用boxed()方法,如intStream.boxed()轉(zhuǎn)化為Stream<Integer>類型妓肢。

三捌省、中間操作

中間操作.png
  1. map:映射,對值進(jìn)行處理碉钠。
  2. flatMap:多個stream連成一個stream纲缓。
  3. filter:過濾,保留通過某項測試的對象放钦。
  4. peak:用于debug,是個中間操作色徘,類似foreach。
  5. limit:返回一個不超過給定長度的流操禀。有狀態(tài)-有界褂策。
  6. skip:跳過前n個元素,若元素不足n個颓屑,返回一個空流斤寂。有狀態(tài)-有界。
  7. sorted:排序揪惦。有狀態(tài)-無界遍搞。
  8. distinct:返回一個元素各異的流。(根據(jù)流所生成的元素的hashCode和equal方法實現(xiàn))器腋。有狀態(tài)-無界溪猿。
  9. peek:將中間變量的值輸出到日志中。

tips:filter和map一起使用時纫塌,盡管它們是兩個獨立的操作诊县,但它們會被合并到同一次遍歷中,這種技術(shù)叫做循環(huán)合并措左。
有狀態(tài)操作:需要知道先前的歷史依痊。

四、終止操作

終止操作.png
  1. collect:收集到集合怎披。
  2. reduce:把流合成一個對象胸嘁。有狀態(tài)-有界。
  3. foreach:消費流中的每個元素凉逛,返回void性宏。
  4. count:返回流中元素的個數(shù)。
  5. allMatch状飞、anyMatch毫胜、noneMatch蝌借、findFirst、findAny
  6. min指蚁、max
public class Test {
    public static void main(String[] args) {

        String str = "hello world";

        List<Character> list = Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).map(c -> (char)c.intValue()).collect(Collectors.toList());

        System.out.println(list);

        //帶初始值的reduce
        Integer len = Stream.of(str.split(" "))
                .map(s -> s.length())
                .reduce(0, (s1, s2) -> s1+s2);
        System.out.println(len);
    }

}

五、收集器

  1. 將流元素歸約和匯總為一個值
    (1)Collectors.toList()自晰、Collectors.toSet()凝化、Collectors.toCollection(TreeSet::new),將流轉(zhuǎn)化為集合酬荞。
    (2)counting()搓劫、minBy()、maxBy()混巧、averagingXX()枪向、summarizingXX(),對流進(jìn)行統(tǒng)計個數(shù)咧党、最小值秘蛔、最大值、平均數(shù)傍衡、匯總等操作深员。
    (3)joining()對流中的每個對象的toString()得到的字符串連接成一個字符串。
    (4)reducing(初始值,轉(zhuǎn)換函數(shù),累積函數(shù))蛙埂,轉(zhuǎn)換函數(shù)的類型為BiFunction<T,T,T>倦畅,reducing()方法是上述這些特殊情況的一般化。

  2. 元素分組
    (1)groupingBy(?)返回一個Map<T, List<T>>
    (2)多級分組:groupingBy(?, groupingBy(?))
    (3)可搭配其他收集器使用:groupingBy(?, counting())绣的、groupingBy(?叠赐,mapping() )
    (4)mapping(映射函數(shù),收集成集合)。
    (5)collectingAndThen(要轉(zhuǎn)換的收集器,轉(zhuǎn)換函數(shù))屡江,將收集器的結(jié)果轉(zhuǎn)化為另一種類型芭概。

  3. 元素分區(qū)
    partitioningBy(?) 返回一個Map<Boolean, List<T>>
    可類比groupingBy()的用法

  4. 自定義收集器
    (1)Collector接口解析

/**
 * 前四個方法都會返回一個被collect方法調(diào)用的函數(shù)
 * 第五個方法提供了一個提示列表,告訴collect方法在執(zhí)行歸約操作時可以應(yīng)用哪些優(yōu)化
 * @param <T>集合中要收集的項目的泛型
 * @param <A>累加器的類型盼理,累加器是在收集過程用于累積部分結(jié)果的對象
 * @param <R>收集操作得到的對象
 */
interface Collector<T, A, R> {
    /**
     * 建立新的結(jié)果容器
     * @return
     */
    Supplier<A> supplier();

    /**
     * 將元素添加到結(jié)果容器
     * @return
     */
    BiConsumer<A, T> accumulator();

    /**
     * 對結(jié)果容器應(yīng)用最終轉(zhuǎn)換
     * @return
     */
    Function<A, R> finisher();

    /**
     * 合并兩個結(jié)果容器
     * @return
     */
    BinaryOperator<A> combiner();

    /**
     *Characteristics是一個包含CONCURRENT谈山、UNORDERED、IDENTITY_FINISH三個項目的枚舉類
     *CONCURRENT accumulator函數(shù)可以從多個線程同時調(diào)用宏怔,且收集器可以并行歸約流奏路。如果沒有聲明為UNORDERED,僅在無序數(shù)據(jù)源才可以并行歸約
     * UNORDERED歸約結(jié)果不受流中項目遍歷和累積順序的影響
     * IDENTITY_FINISH表明完成器方法返回的函數(shù)是一個恒等函數(shù)臊诊。這種情況下鸽粉,累加器對象將會直接用作歸約過程的最終結(jié)果
     * @return
     */
    Set<Characteristics> characteristics();
}

(2)自定義收集器:模擬toList()

public class TestDemo {
    public static void main(String[] args) {
        System.out.println(IntStream.rangeClosed(0, 100).boxed().collect(new ToListCollector<>()));
    }
}

class ToListCollector<T> implements Collector<T, List<T>, List<T>> {

    /**
     * 創(chuàng)建一個空的容器
     * @return
     */
    @Override
    public Supplier<List<T>> supplier() {
        return ArrayList::new;
    }

    /**
     * 加元素添加到容器中
     * @return
     */
    @Override
    public BiConsumer<List<T>, T> accumulator() {
        return List::add;
    }

    /**
     * 恒等函數(shù)
     * @return
     */
    @Override
    public Function<List<T>, List<T>> finisher() {
        return Function.identity();
    }

    /**
     * 將兩個累加器合并并返回
     * @return
     */
    @Override
    public BinaryOperator<List<T>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }

    /**
     * 為收集器添加IDENTITY_FINISH和CONCURRENT標(biāo)志
     * @return
     */
    @Override
    public Set<java.util.stream.Collector.Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(java.util.stream.Collector.Characteristics.IDENTITY_FINISH, java.util.stream.Collector.Characteristics.CONCURRENT));
    }
}

(3)自定義收集器:篩選質(zhì)數(shù)

import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.IntStream;

/**
 * @description: ${todo}
 * @author: wxz1997
 * @date: 18-9-26 下午7:50
 */
public class TestCollector{
    public static void main(String[] args) {
        System.out.println(IntStream.rangeClosed(2, 100).boxed().collect(new PrimeNumbersCollector()));
    }
}

class PrimeNumbersCollector implements Collector<Integer, Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> {

    //創(chuàng)建一個HashMap容器,含有兩個空的List
    @Override
    public Supplier<Map<Boolean, List<Integer>>> supplier() {
        return () -> new HashMap<Boolean, List<Integer>>(){{
            put(true, new ArrayList<>());
            put(false, new ArrayList<>());
        }};
    }

    @Override
    public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
        return (Map<Boolean, List<Integer>> acc, Integer candidate) -> {
            acc.get(isPrime(acc.get(true), candidate)).add(candidate);
        };
    }

    private static boolean isPrime(List<Integer> primes, Integer candidate) {
        int candidateRoot = (int)Math.sqrt((double)candidate);
        return takeWhile(primes, i -> i <= candidateRoot)
                .stream()
                .noneMatch(p -> candidate % p == 0);
    }

    private static <A> List<A> takeWhile(List<A> list, Predicate<A> p){
        int i=0;
        for (A item: list){
            if (!p.test(item)){
                return list.subList(0, i);
            }
            i++;
        }
        return list;
    }

    @Override
    public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
        return Function.identity();
    }

    @Override
    public BinaryOperator<Map<Boolean, List<Integer>>> combiner() {
        return (Map<Boolean, List<Integer>> map1, Map<Boolean, List<Integer>> map2) -> {
            map1.get(true).addAll(map2.get(true));
            map1.get(false).addAll(map2.get(false));
            return map1;
        };
    }

    @Override
    public Set<java.util.stream.Collector.Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(java.util.stream.Collector.Characteristics.IDENTITY_FINISH));
    }
}

六抓艳、并行流

  1. 并行標(biāo)志parallel()触机,串行標(biāo)志sequential(),若多次使用,以最后一個為準(zhǔn)決定該流為串行還是并行儡首。
  2. 并行流內(nèi)部使用了默認(rèn)的ForkJoinPool片任,它默認(rèn)的線程數(shù)量為處理器的數(shù)量。
  3. 自動裝箱和拆箱操作會大大降低性能蔬胯,盡可能使用原始類型流IntStream对供、LongStream、DoubleStream等來避免這些操作氛濒。
    根據(jù)可分解性判斷其是否適合并行


    流的數(shù)據(jù)源和可分解性.png
  4. 工作竊炔 :每個線程都為分配給它的任務(wù)保存一個雙向鏈?zhǔn)疥犃?每完成一個任務(wù),就會從隊列頭上取出下一個任務(wù)開始執(zhí)行∥韪停基于前面所述的原因,某個線程可能早早完成了分配給它的所有任務(wù),也就是它的隊列已經(jīng)空了,而其他的線程還很忙京景。這時,這個線程并沒有閑下來,而是隨機選了一個別的線程,從隊列的尾巴上“偷走”一個任務(wù)。這個過程一直繼續(xù)下去,直到所有的任務(wù)都執(zhí)行完畢,所有的隊列都清空骗奖。這就是為什么要劃成許多小任務(wù)而不是少數(shù)幾個大任務(wù),這有助于更好地在工作線程之間平衡負(fù)載确徙。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市执桌,隨后出現(xiàn)的幾起案子米愿,更是在濱河造成了極大的恐慌,老刑警劉巖鼻吮,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件育苟,死亡現(xiàn)場離奇詭異,居然都是意外死亡椎木,警方通過查閱死者的電腦和手機违柏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來香椎,“玉大人漱竖,你說我怎么就攤上這事⌒蠓ィ” “怎么了馍惹?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長玛界。 經(jīng)常有香客問我万矾,道長,這世上最難降的妖魔是什么慎框? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任良狈,我火速辦了婚禮,結(jié)果婚禮上笨枯,老公的妹妹穿的比我還像新娘薪丁。我一直安慰自己遇西,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布严嗜。 她就那樣靜靜地躺著粱檀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漫玄。 梳的紋絲不亂的頭發(fā)上梧税,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音称近,去河邊找鬼。 笑死哮塞,一個胖子當(dāng)著我的面吹牛刨秆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播忆畅,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼衡未,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了家凯?” 一聲冷哼從身側(cè)響起缓醋,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绊诲,沒想到半個月后送粱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡掂之,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年抗俄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片世舰。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡动雹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跟压,到底是詐尸還是另有隱情胰蝠,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布震蒋,位于F島的核電站茸塞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏查剖。R本人自食惡果不足惜翔横,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梗搅。 院中可真熱鬧禾唁,春花似錦效览、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至掘托,卻和暖如春瘦锹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背闪盔。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工弯院, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泪掀。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓听绳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親异赫。 傳聞我的和親對象是個殘疾皇子椅挣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Java 8函數(shù)式編程學(xué)習(xí)筆記 author:Gavin date:2018/11/09 什么是函數(shù)式編程 在思考...
    安靜點就睡吧閱讀 1,274評論 0 10
  • Java8 in action 沒有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說的函數(shù)式...
    鐵牛很鐵閱讀 1,229評論 1 2
  • Java流庫(java.util.stream) 流提供了一種讓我們可以在比集合更高的概念級別上指定計算的數(shù)據(jù)視圖...
    thorhill閱讀 4,840評論 0 4
  • 流提供了一種讓我們可以在比集合更高的概念級別上指定計算的數(shù)據(jù)試圖塔拳,用來解決“做什么而非怎么做”的問題鼠证。 從迭代到流...
    _gitignore閱讀 1,205評論 0 1
  • Java中的值傳遞和引用傳遞:http://www.reibang.com/p/6d0f7258f2e0 ** 深...
    峰峰小閱讀 248評論 0 0