JAVA8函數(shù)式編程系列2--流(Stream)

????Jav8中,在核心類庫中引入了新的概念沫屡,流(Stream)饵隙。流使得程序媛們得以站在更高的抽象層次上對集合進(jìn)行操作。
????今天沮脖,居士將主要介紹Steam類中對應(yīng)集合上操作的幾個重要的方法金矛。

1、 Steam舉例

????對使用Java的程序媛們勺届,當(dāng)需要處理集合里的每一個數(shù)據(jù)時(shí)驶俊,通常是使用迭代,再對每個返回的元素進(jìn)行處理免姿。比如:

int count = 0;
  ArrayList<String> nameList = new ArrayList<>();
        nameList.add("仁昌居士");
        nameList.add("仁昌居士");
        nameList.add("痕無羽");
        nameList.add("羽無痕");
        for (String name: nameList) {
            if(name.equals("仁昌居士"))
               count++饼酿;
        }

????盡管這段代碼思想上 并不難理解,但是存在幾個問題:
????(1) 從代碼量上來看胚膊,每一次的循環(huán)集合類故俐,都需要重復(fù)寫很多的樣板代碼。
????(2)對于for循環(huán)寫的代碼塊紊婉,有些程序媛可能很難理解其編寫意圖药版。需要閱讀整個循環(huán)體后,才能有一定的理解喻犁。假設(shè)只有一個for循環(huán)槽片,相對理解并不難,但是當(dāng)出現(xiàn)多層嵌套循環(huán)肢础,那理解所花費(fèi)的成本就大幅度提升了还栓。
????分析一下for循環(huán)的實(shí)現(xiàn)原理,可知是通過調(diào)用了iterator()方法传轰,產(chǎn)生了一個Iterator對象蝙云,通過while方法遍歷的顯式調(diào)用這個對象的hasNext()和next()方法。以實(shí)現(xiàn)需求路召。這種遍歷過程叫做外部遍歷勃刨,是一種串行化操作波材。

 Iterator<String> iterator = nameList.iterator();
        while(iterator.hasNext()){
            String name = iterator.next();
            if(name.equals("仁昌居士")){
                 count++;
            }
        }

????注意事項(xiàng):為什么對for循環(huán)叫他外部遍歷而不是外部迭代的原因身隐?可見另一篇文章:還未寫廷区,周末寫。
????相對于外部遍歷贾铝,還有一種方法叫做內(nèi)部遍歷隙轻。通過內(nèi)部遍歷垢揩,將上述代碼實(shí)現(xiàn)為:

 long count = nameList.stream().filter(name -> name.equals("仁昌居士"))
                .count();

????上述代碼實(shí)際是三步玖绿,第一步:nameList創(chuàng)建了一個Stream實(shí)例叁巨,第二步:用fliter操作符過濾找出為“仁昌居士”的name,并轉(zhuǎn)換成另外一個Stream,第三步:把Stream的里面包含的內(nèi)容按照某種算法來成型成一個值,代碼中式用count操作符計(jì)算有幾個這樣的name显蝌。

2、 惰性求值和及早求值

????通常,在Java中調(diào)有一個方法,計(jì)算機(jī)會隨機(jī)執(zhí)行相應(yīng)的操作,比如通過println在終端上輸出一條信息眼坏。Stream里的方法則有些不同。比如說:

nameList.stream().filter(name -> name.equals("仁昌居士"));

????這行代碼并沒有通過fliter得到新的集合,只是對Stream進(jìn)行了描述诚卸,這種方法叫做“惰性求值”方法棠赛,而之后的“.count()”使Stream產(chǎn)生了值的方法,叫做“及早求值”方法。
????最好的驗(yàn)證方式就如下翘狱。
????單純的在filter中加入一條println語句:

   nameList.stream()
                .filter(name -> {
                  System.out.println(name);
                    return name.equals("仁昌居士");
                });

???? 運(yùn)行結(jié)果是程序并沒有輸出對應(yīng)信息赤惊。
????再測試:在后面加入一個及早求值方法勇哗,如count(),將會得到輸出結(jié)果瞧栗。

 nameList.stream()
                .filter(name -> {
                    System.out.println(name);
                    return name.equals("仁昌居士");
                })
                .count();
輸出結(jié)果

????想知道操作符是惰性求值操作符還是及早求值操作符斯稳,只需觀察其返回值海铆,如果返回值是Stream迹恐,則是惰性求值操作符;如果返回值是另一個類型或者是void卧斟,則是及早求值操作符殴边。通過這種多個惰性求值操作符+一個及早求值操作符消費(fèi)為結(jié)尾的鏈來得到想要的值憎茂,這個過程和建造者Builder模式很相似。建造者Builder模式就是通過使用一系列操作設(shè)置屬性和配置锤岸,最后通過一個build方法竖幔,將對象真正創(chuàng)建出來。

3是偷、 常用的Stream操作符

????現(xiàn)在講述幾個比較常用的Stream API拳氢。

3.1 創(chuàng)建Stream操作符

3.1.1 of

????Stream的of操作符,是將一組數(shù)據(jù)生成一個Stream蛋铆。是一個惰性求值操作符馋评。

Stream nameStream =  Stream.of("仁昌居士","痕無羽","羽無痕");

3.1.2 generate

????生成一個無限長度的Stream,其元素的生成是通過給定的Supplier(這個接口可以看成一個對象的工廠刺啦,每次調(diào)用返回一個給定類型的對象)留特,也是一個惰性求值操作符。

Stream.generate(() -> Math.random());

????生成一個無限長度的Stream玛瘸,其中值是隨機(jī)的蜕青。這個無限長度Stream是懶加載,一般這種無限長度的Stream都會配合Stream的limit()方法來用糊渊。

3.1.3 iterate

????iterate操作符生成無限長度的Stream右核,和generator不同的是,其元素的生成是重復(fù)對給定的種子值(seed)調(diào)用用戶指定函數(shù)來生成的渺绒。其中包含的元素可以認(rèn)為是:seed蒙兰,f(seed),f(f(seed))無限循環(huán),也是惰性求值操作符

Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);

????這段代碼就是先獲取一個無限長度的正整數(shù)集合的Stream芒篷,然后取出前10個打印搜变。千萬注意:使用limit方法,不然會無限打印下去针炉。

3.2 轉(zhuǎn)換Stream操作符

3.2.1 map

????map操作符的作用就是將Stream中的每個值進(jìn)行同一個操作的處理后挠他,再將其轉(zhuǎn)換為一個新的Stream,所以是惰性求值操作符篡帕。

 List<String> list =  Stream.of(1,2,3)
                .map(integer -> String.valueOf(integer))
                .collect(Collectors.toList());

????看上面這段代碼可知殖侵,通過map操作符和Lambda表達(dá)式將一個Integer類型的參數(shù)轉(zhuǎn)成了一個String的返回值。參數(shù)和返回值直接不必是同一種類型镰烧,但是Lambda表達(dá)式拢军,必須是Function接口(只包含一個參數(shù)的普通函數(shù)接口)的一個實(shí)例。
????注意事項(xiàng):用map操作符得到的還是Stream怔鳖。

3.2.2 flatMap

????flatMap操作符不同于map操作符將Stream中的值轉(zhuǎn)換為新值茉唉,他能將多個Stream合成一個Stream,返回值也是Stream。是惰性求值操作符度陆。

        ArrayList<Integer> arrayList1 = new ArrayList<>();
        arrayList1.add(1);
        arrayList1.add(2);
        ArrayList<Integer> arrayList2 = new ArrayList<>();
        arrayList2.add(3);
        arrayList2.add(4);
        List<Integer> list3 = Stream.of(arrayList1,arrayList2)
        .flatMap(numbers -> numbers.stream())
        .collect(Collectors.toList());

????通過stream()方法艾凯,將每個ArrayList轉(zhuǎn)換成了Stream對象,其余部分由flatMap操作符處理懂傀,得到的Stream是Stream.of(1趾诗,2,3蹬蚁,4)恃泪。

3.2.3 distinct

????distinct操作符,是對Stream中包含的元素進(jìn)行去重操作(去重邏輯依賴元素的equals方法)犀斋,新生成的Stream中沒有重復(fù)的元素悟泵。是惰性求值操作符。

Stream stream = Stream.of(1, 2, 3, 4,1,2,2,3,4)
                .distinct();

????得到的Stream里面的元素只有1,2,3,4四個闪水。重復(fù)的都被去掉了糕非。

3.2.4 filter

????fliter操作符,上文已經(jīng)提及過了球榆,是用于過濾的惰性求值操作符朽肥。

  List<Integer> list =  Stream.of(1,2,3)
                .filter(integer -> integer >1)
                .collect(Collectors.toList());

????和map操作符相似,filter操作符接受一個函數(shù)為參數(shù)持钉,該函數(shù)通過Lambda表達(dá)式表示衡招,如這段代碼,Lambda表達(dá)式將會對大于1的返回true每强,否則返回false始腾。這段代碼就是通過filter操作符過濾選擇Lambda表達(dá)式返回值為true的元素保留生成新的Stream,并通過collect操作符得到符合要求的List空执。

3.2.5 peek

????peek生成一個包含原Stream的所有元素的新Stream浪箭,同時(shí)會提供一個消費(fèi)函數(shù)(Consumer實(shí)例),當(dāng)最終用及早求值操作符消費(fèi)此Stream時(shí)辨绊,新Stream每個元素都會執(zhí)行給定的消費(fèi)函數(shù)奶栖。是惰性求值操作符。

 nameList.stream()
                .filter(name -> name.equals("仁昌居士"))
                .peek(name -> System.out.println(name))
                .collect(Collectors.toList());

3.2.6 limit

????limit對一個Stream進(jìn)行截?cái)嗖僮髅趴溃@取其前N個元素宣鄙,如果原Stream中包含的元素個數(shù)小于N,那就獲取其所有的元素默蚌,是惰性求值操作符冻晤。

Stream stream = Stream.of(1, 2, 3, 4,5,6,7,8,9,10)
                .limit(3);

????得到的新的Stream的元素只有前3個。后面的被截?cái)嗔恕?/p>

3.2.7 skip

????返回一個跳過原Stream的前N個元素后剩下元素組成的新Stream绸吸,如果原Stream中包含的元素個數(shù)小于N鼻弧,那么返回空Stream设江,是惰性求值操作符。

Stream stream = Stream.of(1, 2, 3, 4,5,6,7,8,9,10)
                .skip(3);

????得到的新的Stream的元素只有后7個温数。前面的3個被跳過不要了。

3.3 成型(Reduce)Stream操作符

???? 成型(Reduce)Stream操作符和.reduce()操作符是兩個東西蜻势。
????成型(Reduce)是個概念撑刺,我將其理解為將Stream在經(jīng)過多次轉(zhuǎn)換操作后確定最終成型得到一個特定的非Stream的結(jié)果。
????而成型(Reduce)Stream操作符就是對Stream反復(fù)使用某個合并操作握玛,把序列中的元素合并成一個整合結(jié)果的操作符够傍。比如:max操作符、min操作符挠铲、sum操作符冕屯、count操作符、reduce()操作符拂苹、collect操作符等等安聘。
????注意事項(xiàng):其中collect操作符和其他幾個操作符不同。他最終成型的結(jié)果是一個可變的容器瓢棒,比如Collection或者StringBuilder浴韭。

3.3.1 max和min

????Stream中進(jìn)行大小比較是比較常用的操作,所以有了max和min操作符脯宿,返回值類型是Optional念颈,這是Java8防止出現(xiàn)NPE的一種可行方法,后面的文章會詳細(xì)介紹连霉,這里就簡單的認(rèn)為是一個容器榴芳,其中可能會包含0個或者1個對象。跺撼。
????查找Stream中的最大或最小元素窟感,就要考慮是用什么作為排序的指標(biāo)。

   Integer integer3 = Stream.of(1, 2, 3, 4)
                .min((x,y) -> x.compareTo(y))
                .get();

????通過比較兩個對象的值的大小歉井,來得到最小值肌括。對于這個指標(biāo),也可以通過Comparator對象酣难。

 Integer integer = Stream.of(1, 2, 3, 4)
                .min(Comparator.naturalOrder())
                .get();

????max和min方法同理谍夭,意思也一目了然,所以不用過多描述憨募,都是及早求值操作符紧索。

3.3.2 sum

????sum操作符不是所有的Stream對象都有的,只有IntStream菜谣、LongStream和DoubleStream是實(shí)例才有珠漂。

 int sum = IntStream.of(1, 2, 3, 4,5,6,7,8,9,10)
                .sum ();

????sum為55晚缩。求和的及早求值操作符。

3.3.3 count

????count操作符不是求Stream中元素的數(shù)量媳危。

long count= Stream.of(0,1, 2, 3, 4,5,6,7,8,9)
                .count();

????count為10荞彼。求元素個數(shù)的及早求值操作符。

3.3.4 reduce

????reduce操作符是及早求值操作符待笑,接受一個元素序列為輸入鸣皂,反復(fù)使用某個合并操作,把序列中的元素合并成一個匯總的結(jié)果暮蹂,其生成的值不是隨意的寞缝,而是根據(jù)指定的計(jì)算模型。像之前的count仰泻、min荆陆、max操作符都是reduce操作。
????reduce方法有三個override的方法集侯。

Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

???? 先來看reduce方法的第一種形式被啼,其方法定義如下:

Optional<T> reduce(BinaryOperator<T> accumulator);

????接受一個BinaryOperator類型的參數(shù),在使用的時(shí)候我們可以用lambda表達(dá)式來棠枉。

Stream.of(1,2,3,4,5,6,7,8,9,10).reduce((sum, item) -> sum + item).get();

????結(jié)果都為55趟据。
???? 可以看到reduce方法接受一個函數(shù),這個函數(shù)有兩個參數(shù)术健,第一個參數(shù)是上次函數(shù)執(zhí)行的返回值(也稱為中間結(jié)果)汹碱,第二個參數(shù)是stream中的元素,這個函數(shù)把這兩個值相加荞估,得到的和會被賦值給下次執(zhí)行這個函數(shù)的第一個參數(shù)咳促。要注意的是:第一次執(zhí)行的時(shí)候第一個參數(shù)的值是Stream的第一個元素,第二個參數(shù)是Stream的第二個元素勘伺。這個方法返回值類型是Optional跪腹。
????再來看reduce方法的第二種形式,其方法定義如下:

T reduce(T identity, BinaryOperator<T> accumulator);

????與第一種變形相同的是都會接受一個BinaryOperator函數(shù)接口飞醉,不同的是其會接受一個identity參數(shù)冲茸,用來指定Stream循環(huán)的初始值。如果Stream為空缅帘,就直接返回該值轴术。另一方面,該方法不會返回Optional钦无,因?yàn)樵摲椒ú粫霈F(xiàn)null逗栽。

Stream.of(1,2,3,4,5,6,7,8,9,10).reduce(1, (sum, item) -> sum + item)).get();

????結(jié)果都為56。
????變形1失暂,未定義初始值彼宠,從而第一次執(zhí)行的時(shí)候第一個參數(shù)的值是Stream的第一個元素鳄虱,第二個參數(shù)是Stream的第二個元素。
???? 變形2凭峡,定義了初始值拙已,從而第一次執(zhí)行的時(shí)候第一個參數(shù)的值是初始值,第二個參數(shù)是Stream的第一個元素摧冀。

<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

????對于第三種變形倍踪,我們先看各個參數(shù)的含義,第一個參數(shù)類型是實(shí)際返回實(shí)例的數(shù)據(jù)類型按价,同時(shí)其為一個泛型也就是意味著該變形的可以返回任意類型的數(shù)據(jù)惭适,第二個參數(shù)累加器accumulator笙瑟,可以使用二元?表達(dá)式(即二元lambda表達(dá)式)楼镐,聲明你在u上累加你的數(shù)據(jù)來源t的邏輯,例如(u,t)->u.sum(t),此時(shí)lambda表達(dá)式的行參列表是返回實(shí)例u和遍歷的集合元素t往枷,函數(shù)體是在u上累加t框产,第三個參數(shù)組合器combiner,同樣是二元?表達(dá)式错洁,(u,t)->u秉宿, 是用來處理并發(fā)操作的。因?yàn)镾tream是支持并發(fā)操作的屯碴,為了避免競爭描睦,對于reduce線程都會有獨(dú)立的result,combiner的作用在于合并每個線程的result得到最終結(jié)果导而。這也說明了了第三個函數(shù)參數(shù)的數(shù)據(jù)類型必須為返回?cái)?shù)據(jù)類型了忱叭。代碼并不好舉例,先不距離今艺,在以后的講解中會提及韵丑。

3.3.5 collect

???? collect操作符:是一個及早求值操作符。它可以把Stream中的要有元素收集到一個結(jié)果容器中(比如Collection)虚缎。先看一下最通用的collect方法的定義(還有其他override方法)撵彻。

<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);

????先來看看這三個參數(shù)的含義:Supplier supplier是一個工廠函數(shù),用來生成一個新的容器实牡;BiConsumer accumulator也是一個函數(shù)陌僵,用來把Stream中的元素添加到結(jié)果容器中;BiConsumer combiner還是一個函數(shù)创坞,用來把中間狀態(tài)的多個結(jié)果容器合并成為一個(并發(fā)的時(shí)候會用到)拾弃。

List<Integer> numsWithoutNull = Stream.of(1,2,3,4,5,6,7,8,9,10)
                    .collect(() -> new ArrayList<Integer>(),(list, item) -> list.add(item),(list1, list2) -> list1.addAll(list2));

????上面這段代碼就是把一個元素是Integer類型的List收集到一個新的List中。進(jìn)一步看一下collect方法的三個參數(shù)摆霉,都是lambda形式的函數(shù)豪椿。
???? 第一個函數(shù)生成一個新的ArrayList實(shí)例奔坟;
????第二個函數(shù)接受兩個參數(shù),第一個是前面生成的ArrayList對象搭盾,二個是stream中包含的元素咳秉,函數(shù)體就是把stream中的元素加入ArrayList對象中。第二個函數(shù)被反復(fù)調(diào)用直到原stream的元素被消費(fèi)完畢鸯隅;
????第三個函數(shù)也是接受兩個參數(shù)澜建,這兩個都是ArrayList類型的,函數(shù)體就是把多個ArrayList容器合并成為一個。
???? 但是上面的collect方法調(diào)用有些復(fù)雜了,有更簡單的override方法揩环,其依賴Collector谐算。

<R, A> R collect(Collector<? super T, A, R> collector);

????進(jìn)一步,Java8還給我們提供了Collector的工具類–Collectors距误,其中已經(jīng)定義了一些靜態(tài)工廠方法,比如:Collectors.toCollection()收集到Collection中, Collectors.toList()收集到List中和Collectors.toSet()收集到Set中,等等奸攻。這樣的靜態(tài)方法還有很多,這里就不一一介紹了虱痕,大家可以直接去看文檔睹耐。下面看看使用Collectors對于代碼的簡化:


List<Integer> numsWithoutNull = Stream.of(1,2,3,4,5,6,7,8,9,10)
                .collect(Collectors.toList());

????這段代碼將of()操作符得到的Stream,用collect(Collectors.toList())操作符從Stream中生成一個List部翘。

4硝训、 性能問題

????完成了上述的講解,會發(fā)現(xiàn)在使用操作符時(shí)新思,會出現(xiàn)對于一個Stream進(jìn)行多次轉(zhuǎn)換操作窖梁,每次都對Stream的每個元素進(jìn)行轉(zhuǎn)換,而且是執(zhí)行多次表牢,這樣時(shí)間復(fù)雜度就是一個for循環(huán)里把所有操作都做掉的N(轉(zhuǎn)換的次數(shù))倍啊窄绒。其實(shí)不是這樣的,轉(zhuǎn)換操作都是lazy的崔兴,多個轉(zhuǎn)換操作只會在成型(Reduce)操作的時(shí)候融合起來彰导,一次循環(huán)完成。我們可以這樣簡單的理解敲茄,Stream里有個操作函數(shù)的集合位谋,每次轉(zhuǎn)換操作就是把轉(zhuǎn)換函數(shù)放入這個集合中,在成型(Reduce)操作的時(shí)候循環(huán)Stream對應(yīng)的集合堰燎,然后對每個元素一次性執(zhí)行所有的操作掏父。

5、總結(jié)

????對于Stream秆剪,單純的書面理解是很難明白的赊淑,碼字看方法才是最好的學(xué)習(xí)方法爵政。所以我的御姐兒,你還是多碼碼代碼吧陶缺。本居士很忙的啊钾挟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市饱岸,隨后出現(xiàn)的幾起案子掺出,更是在濱河造成了極大的恐慌,老刑警劉巖苫费,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件汤锨,死亡現(xiàn)場離奇詭異,居然都是意外死亡百框,警方通過查閱死者的電腦和手機(jī)闲礼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琅翻,“玉大人位仁,你說我怎么就攤上這事柑贞》阶担” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵钧嘶,是天一觀的道長棠众。 經(jīng)常有香客問我,道長有决,這世上最難降的妖魔是什么闸拿? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮书幕,結(jié)果婚禮上新荤,老公的妹妹穿的比我還像新娘。我一直安慰自己台汇,他們只是感情好苛骨,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著苟呐,像睡著了一般痒芝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上牵素,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天严衬,我揣著相機(jī)與錄音,去河邊找鬼笆呆。 笑死请琳,一個胖子當(dāng)著我的面吹牛粱挡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播俄精,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼抱怔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嘀倒?” 一聲冷哼從身側(cè)響起屈留,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎测蘑,沒想到半個月后灌危,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡碳胳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年勇蝙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挨约。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡味混,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诫惭,到底是詐尸還是另有隱情翁锡,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布夕土,位于F島的核電站馆衔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏怨绣。R本人自食惡果不足惜角溃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篮撑。 院中可真熱鬧减细,春花似錦、人聲如沸赢笨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽质欲。三九已至树埠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘶伟,已是汗流浹背怎憋。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绊袋。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓毕匀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親癌别。 傳聞我的和親對象是個殘疾皇子皂岔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)展姐,斷路器躁垛,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • Java8 in action 沒有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說的函數(shù)式...
    鐵牛很鐵閱讀 1,213評論 1 2
  • 3.4 說說相等和內(nèi)部表示 在Lisp中主要有5種相等斷言圾笨,因?yàn)椴皇撬械膶ο蟊粍?chuàng)建的時(shí)候都是相等意義上的相等教馆。數(shù)...
    geoeee閱讀 1,792評論 0 6
  • 這是一篇我關(guān)于 GCD 的使用以及學(xué)習(xí)的總結(jié)文章。持續(xù)更新擂达。感謝諸多大神在此之前寫的各類文章土铺,如果可以,我會盡量把...
    黃穆斌閱讀 326評論 0 1
  • 好的節(jié)目總會給人帶來感動板鬓”螅看朗讀者,每一個故事每一個人都讓我敬佩俭令。今天更是有一份震驚后德。 盡管他們不是青春靚麗的年紀(jì)...
    zzzoou閱讀 252評論 0 0