Java8 stream流操作(上)

本文是我在学习Java8的时候参考下面大佬的文章拷贝的,仅用于个人整理和学习用途
原文地址://www.greatytc.com/p/0bb4daf6c800

一. 什么是 Stream

Stream 中文称为“流”,通过将集合转换为这么一种叫做 “流” 的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作。

换句话说,你只需要告诉流你的要求,流便会在背后自行根据要求对元素进行处理,而你只需要 “坐享其成”。

二. 流操作

image

整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理。

其中数据源便是原始集合,然后将如 List<T> 的集合转换为 Stream<T> 类型的流,并对流进行一系列的中间操作,比如过滤保留部分元素、对元素进行排序、类型转换等;最后再进行一个终端操作,可以把 Stream 转换回集合类型,也可以直接对其中的各个元素进行处理,比如打印、比如计算总数、计算最大值等等

很重要的一点是,很多流操作本身就会返回一个流,所以多个操作可以直接连接起来,我们来看看一条 Stream 操作的代码:

image

如果是以前,进行这么一系列操作,你需要做个迭代器或者 foreach 循环,然后遍历,一步步地亲力亲为地去完成这些操作;但是如果使用流,你便可以直接声明式地下指令,流会帮你完成这些操作。

有没有想到什么类似的?是的,就像 SQL 语句一样,select username from user where id = 1,你只要说明:“我需要 id 是 1 (id = 1)的用户(user)的用户名(username)”,那么就可以得到自己想要的数据,而不需要自己亲自去数据库里面循环遍历查找。

三.方法介绍与实践:

1.获取流:stream() / parallelStream()

   List<Integer> asList = Arrays.asList(10, 20, 30, 40, 50);
   Stream<Integer> stream = asList.stream();
   System.out.println(stream);

输出结果:
clipboard.png

XX.stream() 方法获取的是流,然而我们在实际开发中一般需要获取的是集合

2.流转换为集合:collect(Collectors.toList())

    List<Integer> asList = Arrays.asList(10, 20, 30, 40, 50);
    List<Integer> collect1 = asList.stream().collect(Collectors.toList());
    List<Integer> collect2 = asList.stream().collect(toList());
    System.out.println(collect1);
    System.out.println(collect2);

输出结果: 这两种写法获取的结果都是一样的,第二种为简写


clipboard.png

3.排序 sorted()

eg1:

    List<Integer> asList = Arrays.asList(10, 20, 30, 40, 50);
    List<Integer> asc = asList.stream().sorted().collect(toList()); //正序
    List<Integer> desc = asList.stream().sorted(Comparator.reverseOrder()).collect(toList());//倒序
    System.out.println(asc);
    System.out.println(desc);

输出结果:
clipboard.png

eg2:使用mybatis-plus 查询后对获得的数组中的某个字段进行排序(数据有点多截取了前5条数据进行排序)

    List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<>()); //查询表中所有数据
    ////按照ATag中SerialNumber字段排序
    List<ATag> collect1 = aTags.stream().sorted(Comparator.comparing(ATag::getSerialNumber)).limit(5).collect(toList());
    List<ATag> collect2 = aTags.stream().sorted(Comparator.comparing(ATag::getSerialNumber).reversed()).limit(5).collect(toList());
    collect1.forEach(System.out::println);
    System.out.println("----------分割线------------");
    collect2.forEach(System.out::println);

输出结果:


360截图20200416173801798.jpg

4.去重 distinct()

eg1:

    List<Integer> asList = Arrays.asList(10,10,20,20,30,40,50);
    asList.stream().distinct().collect(toList()).forEach(System.out::println);

输出:

360截图20200416174543939.jpg

eg2:
如果想要对 List<Object> 中某个字段进行去重这里使用 distinct(类::字段) 这种方法是不行的(没有这种筛选条件)
因此,需要借助一下过滤器以及下面学到的filter()方法,实现对 List<Object> 中根据某个字段进行去重
参考文章://www.greatytc.com/p/77c3b503730c

        //过滤器:
        public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
            ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>(16);
            return t -> map.putIfAbsent(keyExtractor.apply(t),Boolean.TRUE) == null;
        }

        List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));//查询表中serial_number=6的数据
        aTags.forEach(System.out::println);
        System.out.println("-------------------------分割线-------------------------");
        aTags.stream().filter(distinctByKey(ATag::getNameZh)).collect(toList()).forEach(System.out::println);

输出 :
xx.jpg

5.过滤 filter()

保留 boolean 为 true 的元素

       //查询表中serial_number=6的数据
        List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
        aTags.forEach(System.out::println);
        System.out.println("-------------------分割线-------------------------");
        aTags.stream().filter(x->x.getNameZh().equals("御坂美琴")).collect(toList()).forEach(System.out::println); 

输出:
11.jpg

6.截取(返回前 n 个元素) limit(long n)

        //查询表中serial_number=6的数据
        List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
        aTags.forEach(System.out::println);
        System.out.println("-------------------分割线-------------------------");
        aTags.stream().limit(2).forEach(System.out::println);

输出结果:
image.png

7.跳过 skip(long n)

        List<Integer> asList = Arrays.asList(10,20,30,40,50);
        System.out.println(asList.stream().skip(1).limit(2).collect(toList()));

这里使用了skip()和limit()

输出结果:
360截图20200417100638948.jpg

tips:

用在 limit(n) 前面时,先去除前 m 个元素再返回剩余元素的前 n 个元素
limit(n) 用在 skip(m) 前面时,先返回前 n 个元素再在剩余的 n 个元素中去除 m 个元素

8. map(T -> R)

将流中的每一个元素 T 映射为 R(类似类型转换)
eg1:

        List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
        aTags.forEach(System.out::println);
        System.out.println("-------------------分割线-------------------------");
        aTags.stream().map(ATag::getNameZh).collect(toList()).forEach(System.out::println);

这里相当于把List<ATag>转换为List<String>

输出:
360截图20200417101622995.jpg
eg2:
        List<Integer> asList = Arrays.asList(10,20,30,40,50);
        List<Integer> collect = asList.stream().map(x -> x + x * 0.1).mapToInt(T -> T.intValue()).boxed().collect(toList());

输出:
1.jpg

9. flatMap(T -> Stream<R>)

将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

List<String> list = new ArrayList<>();
list.add("aaa bbb ccc");
list.add("ddd eee fff");
list.add("ggg hhh iii");
list = list.stream().map(s -> s.split(" ")).flatMap(Arrays::stream).collect(toList());

上面例子中,我们的目的是把 List 中每个字符串元素以" "分割开,变成一个新的 List<String>。
首先 map 方法分割每个字符串元素,但此时流的类型为 Stream<String[ ]>,因为 split 方法返回的是 String[ ] 类型;所以我们需要使用 flatMap 方法,先使用Arrays::stream将每个 String[ ] 元素变成一个 Stream<String> 流,然后 flatMap 会将每一个流连接成为一个流,最终返回我们需要的 Stream<String>

10.anyMatch(T -> boolean)

流中是否有一个元素匹配给定的 T -> boolean 条件

        List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
        aTags.forEach(System.out::println);
        System.out.println("-------------------分割线-------------------------");
        System.out.println(aTags.stream().anyMatch(x -> x.getNameEn().equals("Misaka Miko")));

输出:
360截图20200417102715580.jpg

11. allMatch(T -> boolean)

流中是否所有元素都匹配给定的 T -> boolean 条件,当流中所有元素都符合条件的时候才返回true

        List<Integer> asList = Arrays.asList(10,20,30,40,50);
        System.out.println(asList.stream().allMatch(x -> x >= 10));

输出:
360截图20200417103151762.jpg

12.noneMatch(T -> boolean)

流中是否没有元素匹配给定的 T -> boolean 条件(流中有元素匹配到条件返回true否则返回false)

        List<Integer> asList = Arrays.asList(10,20,30,40,50);
        System.out.println(asList.stream().noneMatch(x -> x >= 10));

输出:


b.jpg

13. findAny() 和 findFirst()

findAny():找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用 parallelStream() 并行时找到的是其中一个元素)
findFirst():找到第一个元素

        List<Integer> asList = Arrays.asList(10,20,30,40,50);
        System.out.println(asList.stream().findFirst());
        System.out.println(asList.stream().findAny());
        System.out.println(asList.parallelStream().findAny());

输出:


c.jpg

14.reduce((T, T) -> T) 和 reduce(T, (T, T) -> T)

用于组合流中的元素,如求和,求积,求最大值等

eg1:其中,reduce 第一个参数 0 代表起始值为 0,lambda (a, b) -> a + b 即将两值相加产生一个新值

        List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
        aTags.forEach(System.out::println);
        System.out.println("-------------------分割线-------------------------");
        System.out.println(aTags.stream().map(ATag::getParentId).reduce(0L, Long::sum));

输出:
2.jpg

eg2:
如果没有初始值,需要考虑结果可能不存在的情况,因此返回的是 Optional 类型如果不需要这种返回类型设置初始值即可

        List<Integer> asList = Arrays.asList(10,20,30,40,50);
        Integer reduce1 = asList.stream().reduce(0, (x, y) -> x + y);
        Integer reduce2 = asList.stream().mapToInt(T -> T.intValue()).boxed().reduce(0, (x, y) -> x + y);
        Optional<Integer> reduce3 = asList.stream().reduce((x, y) -> x + y);
        System.out.println("reduce1:"+reduce1 +"\n"+ "reduce2:"+reduce2+"\n"+"reduce3:"+reduce3);

15. count()

返回流中元素个数,结果为 long 类型

        List<Integer> asList = Arrays.asList(10,20,30,40,50);
        asList.stream().count();

16.遍历流中的每一个元素 forEach()

当然我们也可以通过forEach()遍历流中的每一个元素,然后注入Mapper对流中元素进行CRUD

        List<ATag> aTags = aTagMapper.selectList(new QueryWrapper<ATag>().eq("serial_number",6));
        aTags.forEach(System.out::println);
        System.out.println("-------------------分割线-------------------------");
        aTags.stream().forEach(item->{
            System.out.println(item.getNameZh());
        });

输出:
3.jpg

文章出处
作者:Howie_Y
链接://www.greatytc.com/p/0bb4daf6c800
来源:简书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容