使用Java 8中的Stream

Stream是Java 8 提供的高效操作集合類(Collection)數(shù)據(jù)的API。

1. 從Iterator到Stream

有一個字符串的list术瓮,要統(tǒng)計其中長度大于7的字符串的數(shù)量,用迭代來實現(xiàn):

List<String> wordList = Arrays.asList("regular", "expression", "specified", "as", "a", 
    "string", "must");

int countByIterator = 0;
for (String word: wordList) {
    if (word.length() > 7) {
        countByIterator++;
    }
}

用Stream實現(xiàn):

long countByStream = wordList.stream().filter(w -> w.length() > 7).count();

顯然,用stream實現(xiàn)更簡潔,不僅如此逻悠,stream很容易實現(xiàn)并發(fā)操作肖卧,比如:

long countByParallelStream = wordList.parallelStream().
    filter(w -> w.length() > 7).count();

stream遵循的原則是:告訴我做什么蚯窥,不用管我怎么做。比如上例:告訴stream通過多線程統(tǒng)計字符串長度塞帐,至于以什么順序拦赠、
在哪個線程中執(zhí)行,由stream來負(fù)責(zé)葵姥;而在迭代實現(xiàn)中荷鼠,由于計算的方式已確定,很難優(yōu)化了榔幸。

Stream和Collection的區(qū)別主要有:

  1. stream本身并不存儲數(shù)據(jù)允乐,數(shù)據(jù)是存儲在對應(yīng)的collection里矮嫉,或者在需要的時候才生成的;
  2. stream不會修改數(shù)據(jù)源牍疏,總是返回新的stream蠢笋;
  3. stream的操作是懶執(zhí)行(lazy)的:僅當(dāng)最終的結(jié)果需要的時候才會執(zhí)行,比如上面的例子中鳞陨,結(jié)果僅需要前3個長度大于7
    的字符串昨寞,那么在找到前3個長度符合要求的字符串后,filter()將停止執(zhí)行厦滤;

使用stream的步驟如下:

  1. 創(chuàng)建stream编矾;
  2. 通過一個或多個中間操作(intermediate operations)將初始stream轉(zhuǎn)換為另一個stream;
  3. 通過中止操作(terminal operation)獲取結(jié)果馁害;該操作觸發(fā)之前的懶操作的執(zhí)行窄俏,中止操作后,該stream關(guān)閉碘菜,不能再
    使用了凹蜈;

在上面的例子中,wordList.stream()wordList.parallelStream()是創(chuàng)建stream忍啸,filter()是中間操作仰坦,過
濾后生成一個新的stream,count()是中止操作计雌,獲取結(jié)果悄晃。

2. 創(chuàng)建Stream的方式

  1. 從array或list創(chuàng)建stream:

    Stream<Integer> integerStream = Stream.of(10, 20, 30, 40);
    String[] cityArr = {"Beijing", "Shanghai", "Chengdu"};
    Stream<String> cityStream = Stream.of(cityArr);
    Stream<String> nameStream = Arrays.asList("Daniel", "Peter", "Kevin").
    stream();
    Stream<String> cityStream2 = Arrays.stream(cityArr, 0, 1);
    Stream<String> emptyStream = Stream.empty();

  2. 通過generateiterate創(chuàng)建無窮stream:

    Stream<String> echos = Stream.generate(() -> "echo");
    Stream<Integer> integers = Stream.iterate(0, num -> num + 1);

  3. 通過其它API創(chuàng)建stream:

    Stream<String> lines = Files.lines(Paths.get("test.txt"))

    String content = "AXDBDGXC";
    Stream<String> contentStream = Pattern.compile("[ABC]{1,3}").
    splitAsStream(content);

3. Stream轉(zhuǎn)換

  1. filter()用于過濾,即使原stream中滿足條件的元素構(gòu)成新的stream:

    List<String> langList = Arrays.asList("Java", "Python", "Swift", "HTML");
    Stream<String> filterStream = langList.stream().filter(lang -> lang.equalsIgnoreCase("java"));

  2. map()用于映射凿滤,遍歷原stream中的元素妈橄,轉(zhuǎn)換后構(gòu)成新的stream:

    List<String> langList = Arrays.asList("Java", "Python", "Swift", "HTML");
    Stream<String> mapStream = langList.stream().map(String::toUpperCase);

  3. flatMap()用于將[["ABC", "DEF"], ["FGH", "IJK"]]的形式轉(zhuǎn)換為
    ["ABC", "DEF", "FGH", "IJK"]

    Stream<String> cityStream = Stream.of("Beijing", "Shanghai", "Shenzhen");
    // [['B', 'e', 'i', 'j', 'i', 'n', 'g'], ['S', 'h', 'a', 'n', 'g', 'h', 'a', 'i'], ...]
    Stream<Stream<Character>> characterStream1 = cityStream.
    map(city -> characterStream(city));

    Stream<String> cityStreamCopy = Stream.of("Beijing", "Shanghai", "Shenzhen");
    // ['B', 'e', 'i', 'j', 'i', 'n', 'g', 'S', 'h', 'a', 'n', 'g', 'h', 'a', 'i', ...]
    Stream<Character> characterStreamCopy = cityStreamCopy.
    flatMap(city -> characterStream(city));

其中,characterStream()返回有參數(shù)字符串的字符構(gòu)成的Stream<Character>;

  1. limit()表示限制stream中元素的數(shù)量翁脆,skip()表示跳過stream中前幾個元素眷蚓,concat表示將多個stream連接
    起來,peek()主要用于debug時查看stream中元素的值:

    Stream<Integer> limitStream = Stream.of(18, 20, 12, 35, 89).sorted().limit(3);
    Stream<Integer> skipStream = Stream.of(18, 20, 12, 35, 89).sorted(Comparator.reverseOrder())
    .skip(1);
    Stream<Integer> concatStream = Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5, 6));
    concatStream.peek(i -> System.out.println(i)).count();

peek()intermediate operation反番,所以后面需要一個terminal operation沙热,如count()才能在輸出中
看到結(jié)果;

  1. 有狀態(tài)的(stateful)轉(zhuǎn)換罢缸,即元素之間有依賴關(guān)系篙贸,如distinct()返回由唯一元素構(gòu)成的stream,sorted()返回排
    序后的stream:

    Stream<String> distinctStream = Stream.of("Beijing", "Tianjin", "Beijing").distinct();
    Stream<String> sortedStream = Stream.of("Beijing", "Shanghai", "Chengdu")
    .sorted(Comparator.comparing(String::length).reversed());

4. Stream reduction

reduction就是從stream中取出結(jié)果枫疆,是terminal operation爵川,因此經(jīng)過reduction后的stream不能再使用了。

4.1 Optional

Optional<T>表示或者有一個T類型的對象养铸,或者沒有值雁芙;

  1. 創(chuàng)建Optional對象:

直接通過Optional的類方法:of()/empty()/ofNullable()

Optional<Integer> intOpt = Optional.of(10);
Optional<String> emptyOpt = Optional.empty();
Optional<Double> doubleOpt = Optional.ofNullable(5.5);
  1. 使用Optional對象:

你當(dāng)然可以這么使用:

if (intOpt.isPresent()) {
    intOpt.get();
}

但是,最好這么使用:

doubleOpt.orElse(0.0);
doubleOpt.orElseGet(() -> 1.0);
doubleOpt.orElseThrow(RuntimeException::new);
List<Double> doubleList = new ArrayList<>();
doubleOpt.ifPresent(doubleList::add);

map()方法與ifPresent()用法相同钞螟,就是多個返回值兔甘,flatMap()用于Optional的鏈?zhǔn)奖磉_(dá):

Optional<Boolean> addOk = doubleOpt.map(doubleList::add);
Optional.of(4.0).flatMap(num -> Optional.ofNullable(num * 100))
    .flatMap(num -> Optional.ofNullable(Math.sqrt(num)));

4.2 簡單的reduction

主要包含以下操作: findFirst()/findAny()/allMatch/anyMatch()/noneMatch,比如:

Optional<String> firstWord = wordStream.filter(s -> s.startsWith("Y")).findFirst();
Optional<String> anyWord = wordStream.filter(s -> s.length() > 3).findAny();
wordStream.allMatch(s -> s.length() > 3);
wordStream.anyMatch(s -> s.length() > 3);
wordStream.noneMatch(s -> s.length() > 3);

4.3 reduce方法

  1. reduce(accumulator):參數(shù)是一個執(zhí)行雙目運算的Functional Interface鳞滨,假如這個參數(shù)表示的操作為op洞焙,
    stream中的元素為x, y, z, ...,則reduce()執(zhí)行的就是x op y op z ...拯啦,所以要求op這個操作具有結(jié)合性
    (associative)澡匪,即滿足:(x op y) op z = x op (y op z),滿足這個要求的操作主要有:求和褒链、求積唁情、求最大值、
    求最小值甫匹、字符串連接甸鸟、集合并集和交集等。另外兵迅,該函數(shù)的返回值是Optional的:

    Optional<Integer> sum1 = numStream.reduce((x, y) -> x + y);

  2. reduce(identity, accumulator):可以認(rèn)為第一個參數(shù)為默認(rèn)值抢韭,但需要滿足identity op x = x,所以對于求
    和操作恍箭,identity的值為0刻恭,對于求積操作,identity的值為1扯夭。返回值類型是stream元素的類型:

    Integer sum2 = numStream.reduce(0, Integer::sum);

5. collect結(jié)果

  1. collect()方法:

reduce()collect()的區(qū)別是:

  • reduce()的結(jié)果是一個值鳍贾;
  • collect()可以對stream中的元素進(jìn)行各種處理后,得到stream中元素的值交洗;

Collectors接口提供了很方便的創(chuàng)建Collector對象的工廠方法:

// collect to Collection
Stream.of("You", "may", "assume").collect(Collectors.toList());
Stream.of("You", "may", "assume").collect(Collectors.toSet());
Stream.of("You", "may", "assume").collect(Collectors.toCollection(TreeSet::new));
// join element
Stream.of("You", "may", "assume").collect(Collectors.joining());
Stream.of("You", "may", "assume").collect(Collectors.joining(", "));
// summarize element
IntSummaryStatistics summary = Stream.of("You", "may", "assume")
    .collect(Collectors.summarizingInt(String::length));
summary.getMax();
  1. foreach()方法:

foreach()用于遍歷stream中的元素贾漏,屬于terminal operation
forEachOrdered()是按照stream中元素的順序遍歷藕筋,也就無法利用并發(fā)的優(yōu)勢纵散;

Stream.of("You", "may", "assume", "you", "can", "fly").parallel()
    .forEach(w -> System.out.println(w));
Stream.of("You", "may", "assume", "you", "can", "fly")
    .forEachOrdered(w -> System.out.println(w));
  1. toArray()方法:

得到由stream中的元素得到的數(shù)組,默認(rèn)是Object[]隐圾,可以通過參數(shù)設(shè)置需要結(jié)果的類型:

Object[] words1 = Stream.of("You", "may", "assume").toArray();
String[] words2 = Stream.of("You", "may", "assume").toArray(String[]::new);
  1. toMap()方法:

toMap: 將stream中的元素映射為<key, value>的形式伍掀,兩個參數(shù)分別用于生成對應(yīng)的key和value的值。比如有一個字符串
stream暇藏,將首字母作為key蜜笤,字符串值作為value,得到一個map:

Stream<String> introStream = Stream.
of("Get started with UICollectionView and the photo library".split(" "));
Map<String, String> introMap = 
    introStream.collect(Collectors.toMap(s -> s.substring(0, 1), s -> s));

如果一個key對應(yīng)多個value盐碱,則會拋出異常把兔,需要使用第三個參數(shù)設(shè)置如何處理沖突沪伙,比如僅使用原來的value、使用新的
value县好,或者合并:

Stream<String> introStream = Stream.of("Get started with UICollectionView and the photo library"
    .split(" "));
Map<Integer, String> introMap2 = introStream.collect(Collectors.toMap(s -> s.length(), 
    s -> s, (existingValue, newValue) -> existingValue));

如果value是一個集合围橡,即將key對應(yīng)的所有value放到一個集合中,則需要使用第三個參數(shù)缕贡,將多個value合并:

Stream<String> introStream3 = Stream.of("Get started with UICollectionView and the photo library"
    .split(" "));
Map<Integer, Set<String>> introMap3 = introStream3.collect(Collectors.toMap(s -> s.length(),
        s -> Collections.singleton(s), (existingValue, newValue) -> {
            HashSet<String> set = new HashSet<>(existingValue);
            set.addAll(newValue);
            return set;
        }
    ));
introMap3.forEach((k, v) -> System.out.println(k + ": " + v));

如果value是對象自身翁授,則使用Function.identity(),如:

Map<Integer, Person> idToPerson = people.collect(Collectors
    .toMap(Person::getId, Function.identity()));

toMap()默認(rèn)返回的是HashMap晾咪,如果需要其它類型的map收擦,比如TreeMap,則可以在第四個參數(shù)指定構(gòu)造方法:

Map<Integer, String> introMap2 = introStream.collect(
    Collectors.toMap(s -> s.length(), s -> s, (existingValue, newValue) 
        -> existingValue, TreeMap::new));

6. Grouping和Partitioning

  1. groupingBy()表示根據(jù)某一個字段或條件進(jìn)行分組谍倦,返回一個Map塞赂,其中key為分組的字段或條件,value默認(rèn)為
    list昼蛀,groupingByConcurrent()是其并發(fā)版本:

    Map<String, List<Locale>> countryToLocaleList = Stream.of(Locale.getAvailableLocales())
    .collect(Collectors.groupingBy(l -> l.getDisplayCountry()));

  2. 如果groupingBy()分組的依據(jù)是一個bool條件减途,則key的值為true/false,此時與partitioningBy()等價曹洽,且
    partitioningBy()的效率更高:

    // predicate
    Map<Boolean, List<Locale>> englishAndOtherLocales = Stream.of(Locale.getAvailableLocales())
    .collect(Collectors.groupingBy(l -> l.getDisplayLanguage().equalsIgnoreCase("English")));

    // partitioningBy
    Map<Boolean, List<Locale>> englishAndOtherLocales2 = Stream.of(Locale.getAvailableLocales())
    .collect(Collectors.partitioningBy(l -> l.getDisplayLanguage().equalsIgnoreCase("English")));

  3. groupingBy()提供第二個參數(shù)鳍置,表示downstream,即對分組后的value作進(jìn)一步的處理:

返回set送淆,而不是list:

Map<String, Set<Locale>> countryToLocaleSet = Stream.of(Locale.getAvailableLocales())
          .collect(Collectors.groupingBy(l -> l.getDisplayCountry(), Collectors.toSet()));

返回value集合中元素的數(shù)量:

Map<String, Long> countryToLocaleCounts = Stream.of(Locale.getAvailableLocales())
          .collect(Collectors.groupingBy(l -> l.getDisplayCountry(), Collectors.counting()));

對value集合中的元素求和:

Map<String, Integer> cityToPopulationSum = Stream.of(cities)
        .collect(Collectors.groupingBy(City::getName, Collectors.summingInt(City::getPopulation)));

對value的某一個字段求最大值税产,注意value是Optional的:

Map<String, Optional<City>> cityToPopulationMax = Stream.of(cities)
        .collect(Collectors.groupingBy(City::getName, 
            Collectors.maxBy(Comparator.comparing(City::getPopulation))));

使用mapping對value的字段進(jìn)行map處理:

Map<String, Optional<String>> stateToNameMax = Stream.of(cities)
    .collect(Collectors.groupingBy(City::getState, Collectors.mapping(City::getName, 
        Collectors.maxBy(Comparator.comparing(String::length)))));

Map<String, Set<String>> stateToNameSet = Stream.of(cities)
.collect(Collectors.groupingBy(City::getState, 
    Collectors.mapping(City::getName, Collectors.toSet())));

通過summarizingXXX獲取統(tǒng)計結(jié)果:

Map<String, IntSummaryStatistics> stateToPopulationSummary = Stream.of(cities)
    .collect(Collectors.groupingBy(City::getState, Collectors.summarizingInt(City::getPopulation)));

reducing()可以對結(jié)果作更復(fù)雜的處理,但是reducing()卻并不常用:

Map<String, String> stateToNameJoining = Stream.of(cities)
    .collect(Collectors.groupingBy(City::getState, Collectors.reducing("", City::getName,
        (s, t) -> s.length() == 0 ? t : s + ", " + t)));

比如上例可以通過mapping達(dá)到同樣的效果:

Map<String, String> stateToNameJoining2 = Stream.of(cities)
        .collect(Collectors.groupingBy(City::getState, 
            Collectors.mapping(City::getName, Collectors.joining(", ")
        )));

7. Primitive Stream

Stream<Integer>對應(yīng)的Primitive Stream就是IntStream偷崩,類似的還有DoubleStreamLongStream辟拷。

  1. Primitive Stream的構(gòu)造:of(), range(), rangeClosed(), Arrays.stream():

    IntStream intStream = IntStream.of(10, 20, 30);
    IntStream zeroToNintyNine = IntStream.range(0, 100);
    IntStream zeroToHundred = IntStream.rangeClosed(0, 100);
    double[] nums = {10.0, 20.0, 30.0};
    DoubleStream doubleStream = Arrays.stream(nums, 0, 3);

  2. Object Stream與Primitive Stream之間的相互轉(zhuǎn)換,通過mapToXXX()boxed()

    // map to
    Stream<String> cityStream = Stream.of("Beijing", "Tianjin", "Chengdu");
    IntStream lengthStream = cityStream.mapToInt(String::length);

    // box
    Stream<Integer> oneToNine = IntStream.range(0, 10).boxed();

  3. 與Object Stream相比阐斜,Primitive Stream的特點:

toArray()方法返回的是對應(yīng)的Primitive類型:

int[] intArr = intStream.toArray();

自帶統(tǒng)計類型的方法衫冻,如:max(), average(), summaryStatistics():

OptionalInt maxNum = intStream.max();
IntSummaryStatistics intSummary = intStream.summaryStatistics();

8. Parallel Stream

  1. Stream支持并發(fā)操作,但需要滿足以下幾點:

構(gòu)造一個paralle stream谒出,默認(rèn)構(gòu)造的stream是順序執(zhí)行的隅俘,調(diào)用paralle()構(gòu)造并行的stream:

IntStream scoreStream = IntStream.rangeClosed(10, 30).parallel();

要執(zhí)行的操作必須是可并行執(zhí)行的,即并行執(zhí)行的結(jié)果和順序執(zhí)行的結(jié)果是一致的笤喳,而且必須保證stream中執(zhí)行的操作是線程安
全的:

int[] wordLength = new int[12];
Stream.of("It", "is", "your", "responsibility").parallel().forEach(s -> {
    if (s.length() < 12) wordLength[s.length()]++;
});

這段程序的問題在于为居,多線程訪問共享數(shù)組wordLength,是非線程安全的杀狡。解決的思路有:1)構(gòu)造AtomicInteger數(shù)組蒙畴;
2)使用groupingBy()根據(jù)length統(tǒng)計;

  1. 可以通過并行提高效率的常見場景:

使stream無序:對于distinct()limit()等方法呜象,如果不關(guān)心順序膳凝,則可以使用并行:

LongStream.rangeClosed(5, 10).unordered().parallel().limit(3);
IntStream.of(14, 15, 15, 14, 12, 81).unordered().parallel().distinct();

groupingBy()的操作中碑隆,map的合并操作是比較重的,可以通過groupingByConcurrent()來并行處理蹬音,不過前提是
parallel stream:

Stream.of(cities).parallel().collect(Collectors.groupingByConcurrent(City::getState));

在執(zhí)行stream操作時不能修改stream對應(yīng)的collection上煤;

stream本身是不存儲數(shù)據(jù)的,數(shù)據(jù)保存在對應(yīng)的collection中祟绊,所以在執(zhí)行stream操作的同時修改對應(yīng)的collection,結(jié)果
是未定義的:

// ok
Stream<String> wordStream = wordList.stream();
wordList.add("number");
wordStream.distinct().count();

// ConcurrentModificationException
Stream<String> wordStream = wordList.stream();
wordStream.forEach(s -> { if (s.length() >= 6) wordList.remove(s);});

9. Functional Interface

僅包含一個抽象方法的interface被成為Functional Interface哥捕,比如:Predicate, Function, Consumer等牧抽。
此時我們一般傳入一個lambda表達(dá)式或Method Reference

常見的Functional Interface有:

Functional Interface    Parameter   Return Type     Description Types
Supplier<T>             None        T               Supplies a value of type T
Consumer<T>             T           void            Consumes a value of type T
BiConsumer<T, U>        T,U         void            Consumes values of types T and U
Predicate<T>            T           boolean         A Boolean-valued function
ToIntFunction<T>        T           int             An int-, long-, or double-valued function
ToLongFunction<T>       T           long
ToDoubleFunction<T>     T           double
IntFunction<R>          int         R               A function with argument of type int, long, or double
LongFunction<R>         long
DoubleFunction<R>       double
Function<T, R>          T           R               A function with argument of type T
BiFunction<T, U, R>     T,U         R               A function with arguments of types T and U
UnaryOperator<T>        T           T               A unary operator on the type T
BinaryOperator<T>       T,T         T               A binary operator on the type T

本文來自我的博客遥赚,轉(zhuǎn)載請注明出處扬舒,謝謝!

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凫佛,一起剝皮案震驚了整個濱河市讲坎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愧薛,老刑警劉巖晨炕,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異毫炉,居然都是意外死亡瓮栗,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門瞄勾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來费奸,“玉大人,你說我怎么就攤上這事进陡≡覆” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵趾疚,是天一觀的道長缨历。 經(jīng)常有香客問我,道長糙麦,這世上最難降的妖魔是什么戈二? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮喳资,結(jié)果婚禮上觉吭,老公的妹妹穿的比我還像新娘。我一直安慰自己仆邓,他們只是感情好鲜滩,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布伴鳖。 她就那樣靜靜地躺著,像睡著了一般徙硅。 火紅的嫁衣襯著肌膚如雪榜聂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天嗓蘑,我揣著相機與錄音须肆,去河邊找鬼。 笑死桩皿,一個胖子當(dāng)著我的面吹牛豌汇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泄隔,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼拒贱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佛嬉?” 一聲冷哼從身側(cè)響起逻澳,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎暖呕,沒想到半個月后斜做,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡湾揽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年陨享,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钝腺。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡抛姑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艳狐,到底是詐尸還是另有隱情定硝,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布毫目,位于F島的核電站蔬啡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏镀虐。R本人自食惡果不足惜箱蟆,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刮便。 院中可真熱鬧空猜,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谆沃,卻和暖如春钝凶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唁影。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工耕陷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人据沈。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓哟沫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卓舵。 傳聞我的和親對象是個殘疾皇子南用,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理膀钠,服務(wù)發(fā)現(xiàn)掏湾,斷路器,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • 本文采用實例驅(qū)動的方式肿嘲,對JAVA8的stream API進(jìn)行一個深入的介紹融击。雖然JAVA8中的stream AP...
    浮梁翁閱讀 25,729評論 3 50
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,770評論 0 38
  • Java 8的新特性可以幫助你: 1.使用Java 8可以減少冗長的代碼,讓代碼更易于理解 2.通過方法引用和St...
    Phoenix小彬閱讀 944評論 0 2
  • 了解Stream ? Java8中有兩個最為重要的改變雳窟,一個是Lambda表達(dá)式尊浪,另一個就是Stream AP...
    龍歷旗閱讀 3,306評論 3 4