Java8 新特性(二)- Stream

Stream 用來處理集合數(shù)據(jù)的,通過 stream 操作可以實現(xiàn) SQL 的擁有的大部分查詢功能

Java8 API 官方文檔

下面借助例子迂求,演示 stream 操作

Java userList 列表

private List<User> userList = Arrays.asList(
    new User(101, "小明", 10, "男", "青海省", "西寧市"),
    new User(102, "小青", 12, "女", "寧夏回族自治區(qū)", "銀川市"),
    new User(103, "小海", 8, "男", "西藏自治區(qū)", "拉薩市"),
    new User(108, "阿刁", 18, "女", "西藏自治區(qū)", "拉薩市"),
    new User(104, "小陽", 9, "女", "新疆維吾爾自治區(qū)", "烏魯木齊市"),
    new User(105, "小強", 14, "男", "陜西省", "西安市"),
    new User(106, "小帥", 15, "男", "河北省", "石家莊市"),
    new User(107, "小云", 15, "女", "河北省", "石家莊市")
);

MySQL user 表數(shù)據(jù)

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) PRIMARY KEY,
  `name` varchar(20),
  `age` int(2),
  `gender` varchar(10),
  `province` varchar(100),
  `city` varchar(100)
) ;

INSERT INTO `user` VALUES (101, '小明', 10, '男', '青海省', '西寧市');
INSERT INTO `user` VALUES (102, '小青', 12, '女', '寧夏回族自治區(qū)', '銀川市');
INSERT INTO `user` VALUES (103, '小海', 8, '男', '西藏自治區(qū)', '拉薩市');
INSERT INTO `user` VALUES (104, '小陽', 9, '女', '新疆維吾爾自治區(qū)', '烏魯木齊市');
INSERT INTO `user` VALUES (105, '小強', 14, '男', '陜西省', '西安市');
INSERT INTO `user` VALUES (106, '小帥', 15, '男', '河北省', '石家莊市');
INSERT INTO `user` VALUES (107, '小云', 15, '女', '河北省', '石家莊市');

查詢字段 select - map

// select id from user
userList.stream()
    .map(e -> e.getId())
    .forEach(System.out::println);

至于如何實現(xiàn) select id, name from user 查詢多字段在下面 collector 收集器會詳細講解

條件 where - filter

// select * from user where age<10
userList.stream()
        .filter(e-> e.getAge() < 10)
        .forEach(System.out::println);

// select * from user where age<10 and gender='男'
userList.stream()
        .filter(e->e.getAge() < 10)
        .filter(e->e.getGender()=="男")
        .forEach(System.out::println);

最值顾稀、總和霸褒、數(shù)量万哪、均值(max, min, sum, count, average)

// select max(age), min(age), sum(age), count(age), avg(age) from user
// max
Optional<Integer> maxAge = userList.stream()
                                .map(e -> e.getAge())
                                .max(Comparator.comparingInt(x -> x));
// 等同于
// Optional<Integer> maxAge =  userList.stream()
//  .map(e -> e.getAge())
//  .max((x, y) -> x-y);

// min
Optional<Integer> minAge = userList.stream()
                                .map(e -> e.getAge())
                                .min(Comparator.comparingInt(x -> x));
// sum
Optional<Integer> sumAge = userList.stream()
                                .map(e -> e.getAge())
                                .reduce((e, u) -> e + u);
// count
long count = userList.stream()
                .map(e -> e.getAge())
                .count();
// 平均值=總和/數(shù)量

排序 order by - sorted

// select * from user order by age
userList.stream()
        .sorted(Comparator.comparingInt(User::getAge))
        .forEach(System.out::println);

分頁 limit - skip嗅绰、limit

// select * from user limit 5
userList.stream()
        .limit(5)
        .forEach(System.out::println);

// select * from user limit 5, 5
userList.stream()
        .skip(5)
        .limit(5)
        .forEach(System.out::println);

// select * from user order by age limit 1
userList.stream()
        .sorted(Comparator.comparingInt(User::getAge))
        .limit(1)
        .forEach(System.out::println);
// 或者
Optional<User> minAgeUser = userList.stream()
                                .sorted(Comparator.comparingInt(User::getAge))
                                .findFirst();

是否存在 exists - anymatch

// select exists(select * from user where name='小海')
// 有沒有名字叫“小海”的用戶
boolean exists0 = userList.stream()
                        .anyMatch(e -> e.getName().equals("小海"));

// select not exists(select * from user where name='小海')
// 是不是沒有名字叫“小合蜃澹”的用戶
boolean exists1 = userList.stream()
                        .noneMatch(e -> e.getName().equals("小海"));

// 是不是所有用戶年齡都小于10歲
boolean exists2 = userList.stream()
                        .allMatch(e -> e.getAge() < 10);

收集操作 collect

收集操作就是遍歷 stream 中的元素呵燕,并進行累加處理,即歸約 reduction

歸約的定義

A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list.

前面提到的 max() min() count() reduce() 都屬于 reduction operation

collect() 又和前面這幾種歸約操作有所區(qū)別件相,它是 Mutable reduction 動態(tài)歸約

動態(tài)歸約的定義

A mutable reduction operation accumulates input elements into a mutable result container, such as a Collection or StringBuilder, as it processes the elements in the stream

區(qū)別:動態(tài)歸約將結(jié)果放進 Collection StringBuilder 這樣的動態(tài)容器中再扭,所以稱為動態(tài)歸約。

Stream 接口提供了兩個 collect() 方法

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

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

我們只需理解了第一個方法夜矗,第二個方法就手到擒來了

理解第一個 collect 方法泛范,強烈建議閱讀文檔 動態(tài)歸約的定義,下面只簡單的介紹一下它

三個參數(shù):

  • 供給者 supplier:負責提供動態(tài)容器侯养,例如 Collectors敦跌、StringBuilder
  • 累加器 accumulator:負責將流中的元素做累加處理
  • 合并者 combiner :負責將兩個容器的元素合并在一起

在串行流中,combiner 根本沒有執(zhí)行逛揩,所以隨便寫點啥滿足參數(shù)對象就行柠傍。
如果說串行流是單線程,那么并行流就是多線程了

舉個例子:


     ArrayList<String> strings = new ArrayList<>();
     for (T element : stream) {
         strings.add(element.toString());
     }
    // 等同于
    ArrayList<String> strings = stream.collect(() -> new ArrayList<>(),
                                                (c, e) -> c.add(e.toString()),
                                                (c1, c2) -> c1.addAll(c2));

與其傳遞三個參數(shù)這么麻煩辩稽,還不如直接傳遞一個對象呢惧笛!
這就是第二個 collect() 方法的由來,使用收集器 Collector 來替代三個參數(shù)

實際上逞泄,我們一般不需要自己創(chuàng)建 Collector 對象患整,Java8 提供了一個 Collectors 類,專門提供收集器 Collector 對象喷众。畢竟我們平時能夠使用到的收集操作也就那幾種:轉(zhuǎn)為集合對象各谚、分組、統(tǒng)計到千。

下面以例子演示


在初看 stream 操作的時候昌渤,我被什么創(chuàng)建、中間操作憔四、終止操作膀息、不會改變原對象給弄暈了,我根本不關(guān)心這些了赵,我的第一想法是怎么將操作后的數(shù)據(jù)導出來潜支,重新變成集合對象。

toCollection

不使用收集器的情況下:

List<User> subUserList1 = userList.stream()
                .filter(e -> e.getAge() < 10)
                .filter(e -> e.getGender() == "男")
                .collect(() -> new ArrayList<>(),
                        (c, e) -> c.add(e),
                        (c1, c2) -> c1.addAll(c2));

在 collect() 方法第二個參數(shù)累加器 accumulator (c, e) -> c.add(e) 這里柿汛,對流中元素進行了遍歷冗酿,所以可以把流中元素添加到任意的集合容器中,List、Set已烤、Map 等等

使用 Collectors 工具類提供的收集器:

// toList()
List<User> list = userList.stream()
                .filter(e -> e.getAge() < 10)
                .filter(e -> e.getGender() == "男")
                .collect(Collectors.toList());

// toSet()
Set<User> set = userList.stream()
                .filter(e -> e.getAge() < 10)
                .filter(e -> e.getGender() == "男")
                .collect(Collectors.toSet());

// toCollection()鸠窗,想要返回什么容器,就 new 一個
ArrayList<User> collection = userList.stream()
                .filter(e -> e.getAge() < 10)
                .filter(e -> e.getGender() == "男")
                .collect(Collectors.toCollection(
                    () -> new ArrayList<>()
                ));

這里插播一條新聞:如何將流轉(zhuǎn)為數(shù)組胯究?

Stream 提供了方法 toArray()

Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);

小試牛刀:

Object[] nameArray = userList.stream()
        .map(e -> e.getName())
        .toArray();
Arrays.stream(nameArray)
        .forEach(System.out::println);
// 轉(zhuǎn)為 User 對象數(shù)組
User[] users = userList.stream()
        .filter(e -> e.getGender() == "女")
        .toArray(User[]::new);
Arrays.stream(users)
        .forEach(System.out::println);

toStringBuilder

不使用收集器的情況下:

StringBuilder joinName = userList.stream()
                .map(e -> e.getName())
                .collect(StringBuilder::new,
                        (s, e) ->  s = s.length() > 0 ? s.append("-" + e) : s.append(e),
                        (s1, s2) -> s1.append(s2)
                );

誰能告訴我在Java中怎么單獨使用三元運算符稍计?s = s.length() > 0 ? s.append("-" + e) : s.append(e) 我想把 s = 省略掉,但 Java 中不行

使用 Collectors 類提供的收集器:

String joinName1 = userList.stream()
                .map(e -> e.getName())
                .collect(Collectors.joining());

String joinName2 = userList.stream()
    .map(e -> e.getName())
    .collect(Collectors.joining("-"));

String joinName3 = userList.stream()
    .map(e -> e.getName())
    .collect(Collectors.joining("-", "[", "]"));

至于 Collectors.joining() 參數(shù)分別代表什么含義裕循,看一下它們的參數(shù)名稱臣嚣,就明白了

public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,    // 分隔符
                                                             CharSequence prefix,   // 前綴
                                                             CharSequence suffix)   // 后綴

toMap

在 Collectors 中一共有3個 toMap(),它們用來處理不同的問題

兩個參數(shù)的 toMap

 Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper) {
        return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
    }

參數(shù) keyMapper 用來獲取key;valueMapper 用來獲取 value

它的內(nèi)部調(diào)用了四個參數(shù)的 toMap() 方法

例子

Map<Integer, User> map1 = userList.stream()
    .collect(Collectors.toMap(e -> e.getId(), Function.identity()));
System.out.println(map1);
// Function.identity() 等價于 e -> e

// select id, name, gender from user
Map<Integer, Map<String, Object>> map2 = userList.stream()
    .collect(Collectors.toMap(e -> e.getId(), e -> {
        Map<String, Object> map = new HashMap<>();
        map.put("gender", e.getGender());
        map.put("name", e.getName());
        map.put("id", e.getId());
        return map;
    }));
System.out.println(map2);

你:如果 key 沖突了咋辦剥哑?
Java8:你想咋辦就咋辦

三個參數(shù)的 toMap

Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
        return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
    }

第三個參數(shù) mergeFunction 就是用來處理 key 鍵沖突的

內(nèi)部也是調(diào)用了四個參數(shù)的 toMap() 方法

例子

// 如果 key 沖突硅则,那么將沖突的 value 值拼接在一起
Map<String, String> map3 = userList.parallelStream()
                .collect(Collectors.toMap(
                    e -> e.getGender(), 
                    e -> e.getName(), 
                    (o1, o2) -> o1 + ", " + o2
                    )
                );
System.out.println(map3);

你:我想自己 new 一個 Map 對象

四個參數(shù)的 toMap

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                            Function<? super T, ? extends U> valueMapper,
                            BinaryOperator<U> mergeFunction,
                            Supplier<M> mapSupplier) 

參數(shù) mapSupplier 用來提供返回容器

例子

LinkedHashMap<String, String> map4 = userList.parallelStream()
                .collect(Collectors.toMap(e -> e.getGender(), e -> e.getName(), (o1, o2) -> o1 + ", " + o2, LinkedHashMap::new));
System.out.println(map4);

reducing

單參數(shù)和兩參數(shù)的 reducing()

Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)
Collector<T, ?, U> reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op)

以例子具體解釋這兩個方法

Optional<String> names1 = userList.stream()
                .map(User::getName)
                .collect(Collectors.reducing((e1, e2) -> e1 + "," + e2));
System.out.println(names1.get());

// 等同于
String names2 = userList.stream()
            .collect(Collectors.reducing(
                    "", (e) -> e.getName(), (e1, e2) -> e1 + "," + e2)
                    );
System.out.println(names2);

輸出結(jié)果:

小明,小青,小海,阿刁,小陽,小強,小帥,小云
,小明,小青,小海,阿刁,小陽,小強,小帥,小云

參數(shù) identity 表示返回結(jié)果的初始值

三參數(shù)的 reducing()

reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op)

identity 是初始值,mapper 會對元素先進行一次處理株婴,然后 op 對元素進行歸約操作

注意: 返回類型要和參數(shù) identity 的一致怎虫。
你也許會納悶,為什么有的返回一個 Optional<String> 類型數(shù)據(jù)困介,而有的就返回了 String
因為含有參數(shù) identity 的 reduing 方法中返回值有初始值大审,也就是 identity,所以不會出現(xiàn)空的情況

下面Collectors 提供的一些常用歸約收集器

// minBy座哩、maxBy
Optional<User> minAgeUser = userList.stream()
                .collect(Collectors.minBy((o1, o2) -> o1.getAge() - o2.getAge()));

// counting
Long count = userList.stream()
    .collect(Collectors.counting());

// summingInt徒扶、summingLong、summingDouble根穷、
Integer sumAge = userList.stream()
    .collect(Collectors.summingInt(User::getAge));

// averagingInt姜骡、averagingLong、averagingDouble
// 平均值內(nèi)部是總值/數(shù)量屿良,所以返回值是浮點數(shù) dobule
Double avgAge = userList.stream()
    .collect(Collectors.averagingInt(User::getAge));

你也許覺得每次都要執(zhí)行一遍 minBy圈澈、maxBy、counting尘惧、summingXxx康栈、averagingXxx 這些太麻煩了,有沒有一次執(zhí)行就獲取所有這些方法結(jié)果褥伴?
有的。這就是 summarizingXxx

Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)
Collector<T, ?, LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper)
Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper)

這里不演示了漾狼,實際上你看一下 XxxSummaryStatistics 這些類就明白了重慢,比如

public class IntSummaryStatistics implements IntConsumer {
    private long count;
    private long sum;
    private int min = Integer.MAX_VALUE;
    private int max = Integer.MIN_VALUE;
    ...
}

group by

最最激動人心的時候到了,我們要使用分組了Q吩辍K契狻!

Map<String, List<User>> map = userList.stream()
                .collect(Collectors.groupingBy(User::getGender));

SQL 中的 group by 結(jié)果集中只能包含分組字段和聚合函數(shù)計算結(jié)果,這段代碼比它更加全面

我們使用如下語句輸出結(jié)果

map.keySet().stream()
        .forEach((e) -> {
            System.out.println(e + "=" + map.get(e));
        });

顯示結(jié)果:

女=[User{id=102, name='小青', age=12, gender='女', province='寧夏回族自治區(qū)', city='銀川市'}, User{id=108, name='阿刁', age=18, gender='女', province='西藏自治區(qū)', city='拉薩市'}, User{id=104, name='小陽', age=9, gender='女', province='新疆維吾爾自治區(qū)', city='烏魯木齊市'}, User{id=107, name='小云', age=15, gender='女', province='河北省', city='石家莊市'}]
男=[User{id=101, name='小明', age=10, gender='男', province='青海省', city='西寧市'}, User{id=103, name='小海', age=8, gender='男', province='西藏自治區(qū)', city='拉薩市'}, User{id=105, name='小強', age=14, gender='男', province='陜西省', city='西安市'}, User{id=106, name='小帥', age=15, gender='男', province='河北省', city='石家莊市'}]

它真的分組了:搜俊囚戚!這是真正的分組

那怎么對分組中的元素進行操作呢,像 SQL 那樣轧简?驰坊?

完全不用擔心,Collectors 提供了三個 groupBy 方法返回分組收集器

Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T,? extends K> classifier)
    
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T,? extends K> classifier, 
                                      Collector<? super T,A,D> downstream)
    
Collector<T, ?, M> groupingBy(Function<? super T,? extends K> classifier, 
                              Supplier<M> mapFactory, 
                              Collector<? super T,A,D> downstream)

讓我們放飛想象的翅膀哮独,思考一下這幾個參數(shù)分別有什么用拳芙。

downstream ?有 down 就表示有 up皮璧。那么誰是 upstream舟扎,很明顯是 userList.stream,那么 downstream 就是分組集合的流嘍悴务。猜測 downstream 收集器是對分組中的元素進行歸約操作的睹限,就像是分組 SQL 語句字段中的聚合操作一樣。

// select gender, count(*) from user group by gender
Map<String, Long> map2 = userList.stream()
                .collect(Collectors.groupingBy(User::getGender, Collectors.counting()));
System.out.println(map2);

輸出結(jié)果確實不出所料讯檐!這就是證明參數(shù) downstream 確實是分組集合元素的收集器羡疗。

Supplier<M> mapFactory 這函數(shù)式接口方法不會有參數(shù)傳入,所以不會操作集合元素裂垦;它只是返回一個變量顺囊。同志們,注意觀察三個方法返回值蕉拢,前二者都指定了 Map 作為歸約操作的返回類型特碳,而第三個要我們自己定義,使用 mapFactory 提供返回的數(shù)據(jù)容器

兩參數(shù)的 groupingBy 方法其實是調(diào)用了三參數(shù)的 groupingBy 方法(而單參數(shù) groupingBy 調(diào)用了兩參數(shù)的 groupingBy)

    Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
                                          Collector<? super T, A, D> downstream) {
        return groupingBy(classifier, HashMap::new, downstream);
    }

groupingBy 經(jīng)常使用 Collectors.mapping() 處理分組集合

Map<String, List<Map<String, Object>>> map4 = userList.stream()
    .collect(Collectors.groupingBy(
        User::getGender,
        Collectors.mapping(e -> {
            Map<String, Object> m = new HashMap<>();
            m.put("name", e.getName());
            m.put("id", e.getId());
            return m;
        }, Collectors.toList())
    ));
System.out.println(map4);

輸出結(jié)果:

{女=[{name=小青, id=102}, {name=阿刁, id=108}, {name=小陽, id=104}, {name=小云, id=107}], 男=[{name=小明, id=101}, {name=小海, id=103}, {name=小強, id=105}, {name=小帥, id=106}]}

partitionBy

實際上也是分組晕换,只不過 partitionBy 是按照布爾值(真假)來分組

Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {
    return partitioningBy(predicate, toList());
}

Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
                                                    Collector<? super T, A, D> downstream)

例子:大于10歲為一組午乓,小于等于10的為一組

Map<Boolean, List<User>> map1 = userList.stream()
                .collect(Collectors.partitioningBy(e -> e.getAge() > 10));

例子:統(tǒng)計大于10歲的有多少人,小于等于10歲的有多少人

Map<Boolean, Long> map2 = userList.stream()
            .collect(Collectors.partitioningBy(e -> e.getAge() > 10, Collectors.counting()));

第二個參數(shù) downstream 用來處理分組集合

結(jié)語

Java8 提供的 stream 幾乎是窮盡了所有集合元素能有的操作闸准,起碼是窮盡了我腦海里對集合元素操作的所有想象

這篇文章也列舉了 stream 絕大部分的功能益愈,盡量寫得通俗易懂,但讀者理解起來可能還是有模糊的地方夷家,這時建議大家參考 Java8 API 官方文檔 蒸其,多做幾個 Demo 加深理解

不要過度使用

stream 是為了方便集合操作,簡化代碼而推出的库快,提升代碼執(zhí)行效率并不是它的目的摸袁。

雖然,并行流會對代碼的執(zhí)行效率有較大的提升(尤其是數(shù)據(jù)量非常大的時候)义屏,但也依賴于計算機的CPU配置靠汁。

Stream 能實現(xiàn)的功能蜂大,for 循環(huán)都能實現(xiàn),只是 Stream 代碼一般比較簡潔蝶怔,可讀性強奶浦。但在某些情況下,使用 for 循環(huán)要比 Stream 要簡潔代碼邏輯清晰

舉個例子:

    private List<Order> orderList = Arrays.asList(
            new Order(103),
            new Order(106),
            new Order(107),
            new Order(104),
            new Order(102),
            new Order(103),
            new Order(102),
            new Order(101),
            new Order(104),
            new Order(102),
            new Order(105)
    );
    // 現(xiàn)根據(jù) userId 設(shè)置 Order 對象的 name 屬性
    
    // 使用 stream
    List<Order> newOrderList = orderList.stream()
                .map(o -> userList.stream()
                        .filter(u -> u.getId() == o.getUserId())
                        .findFirst()
                        .map(u -> {
                            o.setUserName(u.getName());
                            return o;
                        })
                        .orElse(o))
                .collect(Collectors.toList());
    newOrderList.stream().forEach(System.out::println);

    // 使用 for 循環(huán)
    for (Order o : orderList) {
        for (User u : userList) {
            if (o.getUserId() == u.getId()) {
                o.setUserName(u.getName());
                break;
            }
        }
    }
    orderList.stream().forEach(System.out::println);

在這個例子中踢星,使用 for 循環(huán)要比 使用 stream 干凈利落的多澳叉,代碼邏輯清晰簡明,可讀性也比 stream 好斩狱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耳高,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子所踊,更是在濱河造成了極大的恐慌泌枪,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秕岛,死亡現(xiàn)場離奇詭異碌燕,居然都是意外死亡,警方通過查閱死者的電腦和手機继薛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門修壕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人遏考,你說我怎么就攤上這事慈鸠。” “怎么了灌具?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵青团,是天一觀的道長。 經(jīng)常有香客問我咖楣,道長督笆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任诱贿,我火速辦了婚禮娃肿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘珠十。我一直安慰自己料扰,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布焙蹭。 她就那樣靜靜地躺著晒杈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪壳嚎。 梳的紋絲不亂的頭發(fā)上桐智,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音烟馅,去河邊找鬼说庭。 笑死,一個胖子當著我的面吹牛郑趁,可吹牛的內(nèi)容都是我干的刊驴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雪营,長吁一口氣:“原來是場噩夢啊……” “哼囊榜!你這毒婦竟也來了挖帘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤躲惰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后变抽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體础拨,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年绍载,在試婚紗的時候發(fā)現(xiàn)自己被綠了诡宗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡击儡,死狀恐怖塔沃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阳谍,我是刑警寧澤蛀柴,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站边坤,受9級特大地震影響名扛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茧痒,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一肮韧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧旺订,春花似錦弄企、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至樱调,卻和暖如春约素,著一層夾襖步出監(jiān)牢的瞬間届良,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工圣猎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留士葫,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓送悔,卻偏偏與公主長得像慢显,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子欠啤,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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

  • Int Double Long 設(shè)置特定的stream類型荚藻, 提高性能,增加特定的函數(shù) 無存儲洁段。stream不是一...
    patrick002閱讀 1,273評論 0 0
  • Stream API是Java8中處理集合的關(guān)鍵組件应狱,提供了各種豐富的函數(shù)式操作。 Stream的創(chuàng)建 任何集合都...
    fengshunli閱讀 519評論 0 2
  • 原文地址: 深藍至尊 一. 流式處理簡介 在我接觸到java8流式處理的時候,我的第一感覺是流式處理讓集合操作變得...
    咻咻咻i閱讀 1,147評論 0 0
  • 原文地址 http://blog.csdn.net/myherux/article/details/7185511...
    Vissioon閱讀 678評論 0 0
  • Stream是Java 8 提供的高效操作集合類(Collection)數(shù)據(jù)的API纽疟。 1. 從Iterator到...
    nkcoder閱讀 5,599評論 2 24