Java Stream 集合的快速处理

stream 可理解为一种数据结构,用于方便地对集合进行处理,比如:将集合按照某个字段进行分组,将集合按照某个字段进行排序,将集合中的某个字段值单独拿出来作为一个新的集合,等等。一般步骤为:先将集合转为 Stream 结构,再调用 Stream 对象自身的方法对 Stream 进行处理。至于能对集合做哪些处理呢? 下面看看 Stream 接口的常用方法。

1. collect 方法

收集集合中的某些数据生成新的数据结构,因此 collect 方法返回的是收集的数据,数据具体是什么数据结构,那就看 collect 方法的参数是什么了。
<R, A> R collect(Collector<? super T, A, R> collector); 参数是一个 Collector 对象,所以要先了解 Collectors。

1.1 Collectors 类

Collectors 类包含了几个常用方法,用于表明需要将从集合中收集到的数据形成什么样的数据结构,这些方法的返回值都是 Collector,注意不是 Collectors ,没有 s。
toList() :形成 List 结构。
toSet():形成 Set 结构。
toCollection:形成 Collection 结构。
toMap:形成 Map 结构。
toConcurrentMap:形成 ConcurrentMap 结构。
joining:将收集到的数据连接起来。
collectingAndThen:将收集到的数据再做一次自定义处理。
counting:收集到的数据的总数量。
minBy:收集到的数据中最小的。
maxBy:收集到的数据中最大的。
groupingBy:按照某个字段进行分组。
partitioningBy:按照自定义条件,将收集大到的数据分为两部分。

接下来是实例来说明每个方法的使用。

1.1.1 toList

		List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        List<String> list1 = list.stream().collect(Collectors.toList());
        System.out.println(list1);

总结:将 list 收集起来,实际上多此一举,这里只是举个例子说明 toList 的用法,实用的用法可以接着往下看。

1.1.2 toSet

		List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        Set<String> list1 = list.stream().collect(Collectors.toSet());
        System.out.println(list1);

总结:简单说明用法而已。

1.1.3 toCollection

        List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        Collection<String> list1 = list.stream().collect(Collectors.toCollection(LinkedList::new));
        System.out.println(list1);

总结:Collection 是一个接口,必须有具体实现,那用什么类实现呢?可以指定,比如上述代码中指定用 LinkedList 实现。

1.1.4 toMap

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "haha");
        Person two = new Person(15, "erer");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);

        Map<Integer, Person> map = list.stream().collect(Collectors.toMap(Person::getAge, o->o));
        Map<Integer, Person> map1 = list.stream().collect(Collectors.toMap(Person::getAge, Function.identity()));
        Map<Integer, String> map2 = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName));
        Map<String, String> map3 = list.stream().collect(Collectors.toMap(o->o.getName() + "_" + "后缀", Person::getName));
        System.out.println(map);
        System.out.println(map1);
        System.out.println(map2);
        System.out.println(map3);

运行结果:

{12=com.dgh.Application$Person@10f87f48, 13=com.dgh.Application$Person@b4c966a, 	15=com.dgh.Application$Person@2f4d3709}
{12=com.dgh.Application$Person@10f87f48, 13=com.dgh.Application$Person@b4c966a, 15=com.dgh.Application$Person@2f4d3709}
{12=haha, 13=sans, 15=erer}
{haha_后缀=haha, erer_后缀=erer, sans_后缀=sans}

总结:map 和 map1 是等效的,因为 Function.identity() 就等效于 o->o。toMap 是用于将集合形成 Map 结构,Map 的 key 可以指定,value 也可以指定,比如上述代码中,让 age 作为 key,age 对应的 person 对象作为 value。
注意,Map 中每一对 <key, value> 都必须是唯一的,不能为重复的 key,否则会报错。
如果 key 有重复的,应该这么解决:

        List<Person> list = new LinkedList<>();
        Person one = new Person(13, "haha");
        Person two = new Person(15, "erer");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        Map<Integer, String> map = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName, (o1, o2)->o1));
        Map<Integer, String> map1 = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName, (o1, o2)->o2));
        Map<Integer, String> map2 = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName, (o1, o2)->o1 + "_" + o2));
        System.out.println(map); //保留第一个
        System.out.println(map1); // 保留最后一个
        System.out.println(map2); // 都保留,但是 value 合并在一起       

运行结果:

{13=haha, 15=erer}
{13=sans, 15=erer}
{13=haha_sans, 15=erer}

还有一点需要特别注意:如果某个 person 对象的 age = null,那么生成的 Map 结构中,对应的 key 就是 null,也要考虑 null 重复的情况。
toMap 默认是用 HashMap 实现的,当然也可以指定用另外的来实现,比如 TreeMap。举例:

Map<Integer, String> map1 = list.stream().collect(Collectors.toMap(Person::getAge, Person::getName, (o1, o2)->o2, TreeMap::new));

1.1.5 joining

        List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        String list1 = list.stream().collect(Collectors.joining());
        String list2 = list.stream().collect(Collectors.joining("->"));
        String list3 = list.stream().collect(Collectors.joining("->", "{", "}"));
        System.out.println(list1);
        System.out.println(list2);
        System.out.println(list3);

运行结果:

onetwothreefour
one->two->three->four
{one->two->three->four}

总结:joining 方法可以将集合中的元素都连接起来,可以设置元素之间的分割符,可以指定连接的起始符和末尾符。

1.1.6 collectingAndThen

        List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        Integer len = list.stream().collect(Collectors.collectingAndThen(Collectors.joining("--"), o->o.length()));
        System.out.println(len);

运行结果(先将集合中的元素用“–”连接起来形成一个字符串,再获取字符串的长度):

21

总结:collectingAndThen 方法包含2个参数,第一个参数是 Collector 对象,表示要对集合做什么样的收集,第二个参数是一个函数,表示对收集到的数据做什么样的处理,且此函数的返回值就是最终的值,比如上述代码中,o->o.length() 的返回值就是 Integer len,o 代表第一个参数收集到的新集合。

1.1.7 counting

        List<String> list = new LinkedList<>();
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        long len = list.stream().collect(Collectors.counting());
        System.out.println(len);

运行结果:

4

总结:counting 方法用于统计 stream 中元素的总数量。

1.1.8 minBy

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "ere");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        
        Optional<Person> op = list.stream().collect(Collectors.minBy(Comparator.comparing(Person::getAge)));
        Person min = null;
        if(op.isPresent()) min = op.get();
        Optional<Person> op2 = list.stream().collect(Collectors.minBy((o1, o2)->{
            if(o1.getName().length() > o2.getName().length()) return 1;
            else return -1;
        }));
        Person min2 = null;
        if(op2.isPresent()) min2 = op2.get();
        System.out.println(min.getName()); //集合中 age 最小的那个人的 name
        System.out.println(min2.getName()); //集合中 name 最短的那个人的 name

运行结果:

hahah
ere

总结:minBy 是一种求 stream 中最小元素的策略,如果用于区分大小的字段比较简单,那么可以直接使用类似 Comparator.comparing(Person::getAge) 的方式,如果元素之间大小是要通过非正常方式进行判定,那么可以使用上述代码中 min2 的方式,使用 lambda 表达式来设置。

1.1.9 groupingBy

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(12, "ere");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        Map<Integer, List<Person>> map = list.stream().collect(Collectors.groupingBy(Person::getAge));
        Map<Integer, List<Person>> map2 = list.stream().collect(Collectors.groupingBy(o->o.getAge() + 1));
        System.out.println(map);
        System.out.println(map2);

运行结果:

{12=[com.dgh.Application$Person@723279cf, com.dgh.Application$Person@10f87f48], 13=[com.dgh.Application$Person@b4c966a]}
{13=[com.dgh.Application$Person@723279cf, com.dgh.Application$Person@10f87f48], 14=[com.dgh.Application$Person@b4c966a]}

总结:groupingBy 是按照某个形式对 stream 中的元素进行分组,比如上述代码中,map 就是以 age 进行分组,map2 则是以 age + 1 进行分组,也就是说,你可以自定义分组的标准。

1.1.11 partitioningBy

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(12, "ere");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        Map<Boolean, List<Person>> map = list.stream().collect(Collectors.partitioningBy(o->o.getAge()==12));
        Map<Boolean, Long> map2 = list.stream().collect(Collectors.partitioningBy((o->o.getAge()==12), Collectors.counting()));
        System.out.println(map.get(true).size() + "-------" + map.get(false).size());
        System.out.println(map2.get(true) + "-------" + map2.get(false));

运行结果:

2-------1
2-------1

总结:partitioningBy 是按照某个条件对 stream 中的元素进行划分,划分为两部分:满足条件的 + 不满足条件的,以 Map 的形式存储起来,key = true 的那部分为满足条件的,key = false 的那部分为不满足条件的。当然,划分之后,还可以对划分了的部分再做一次操作,比如上述代码中,统计划分之后两部分的元素个数,key = true 的那个 Long 值表示满足条件的那些元素的总个数。

2. filter 方法

filter 方法用于设置过滤条件(lambda 表达式),将 stream 中符合条件的元素收集起来,形成一个新的 stream,并返回 stream。

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(12, "ere");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        List<Person> hh = list.stream().filter(o->o.getAge() > 12).collect(Collectors.toList());
        System.out.println("大于12的人有多少个:" + hh.size() + "       第一个人的名字:" + hh.get(0).getName());

运行结果:

大于12的人有多少个:1       第一个人的名字:sans

3. map 方法

map 方法用于收集 stream 中元素的某个字段,形成新的 stream,并返回 stream。

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(12, "ere");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        List<String> names = list.stream().map(Person::getName).collect(Collectors.toList());
        System.out.println(names);

运行结果:

[hahah, ere, sans]

4. distinct 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "hahah");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        List<String> names = list.stream().map(Person::getName).distinct().collect(Collectors.toList());
        System.out.println(names);

运行结果:

[hahah, sans]

总结:distinct 方法用于将 stream 中的元素去重,所以一定要注意 equals 方法的重写(如果有需要)。

5. sorted 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "hahah");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        List<Integer> ages = list.stream().map(Person::getAge).sorted().collect(Collectors.toList());
        List<Person> persons = list.stream().sorted(Comparator.comparing(Person::getAge)).collect(Collectors.toList());
        System.out.println(ages);
        System.out.println(persons.get(0).getAge() + "," + persons.get(1).getAge() + "," + persons.get(2).getAge());

运行结果:

[12, 13, 15]
12,13,15

总结:sorted 排序方法默认是升序,而且要排序的 stream 中的元素必须实现 Compareable 接口,也可以按照自己的需求,将sorted 方法的参数设置为比较规则 lambda 表达式。

6. peek 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        List<Person> hehe = list.stream().peek(o->o.setAge(o.getAge() + 5)).filter(o->o.getAge() >= 18).collect(Collectors.toList());
        System.out.println(hehe.get(0).getName() + "," + hehe.get(1).getName());

运行结果:

wwww,sans

总结:peek 方法的参数也是一个 lambda 表达式,但是参数类型是 Consumer,即 lambda 表达式的是不能有返回值的(void),另外说一下,Function 接口也是一种函数式接口,只是 Function 这种 lambda 表达式有返回值。因此,peek 方法是用于对 stream 中元素的处理。比如上述代码,用 peek 给集合中每个元素的 age 都加 5,然后再用 filter 过滤出年龄在 18 及以上的。

7. limit 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        List<Person> hehe = list.stream().limit(2).collect(Collectors.toList());
        System.out.println(hehe.get(0).getName() + "," + hehe.get(1).getName());

运行结果:

hahah,wwww

总结:limit 方法用于获取 stream 中前多少个元素,假设为 n,如果 n 大于 stream 中的总元素个数,那么就返回 stream 中的所有元素。

8. skip 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        List<Person> hehe = list.stream().skip(2).collect(Collectors.toList());
        System.out.println(hehe.get(0).getName());

运行结果:

sans

总结:skip 方法是用于从 stream 的开头位置跳过多少个元素之后,到达的新起始位置,比如跳过2个元素。

9. forEach 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        System.out.println("顺序流的遍历结果:");
        list.stream().forEach(o->System.out.println(o.getName()));
        System.out.println("并行流的遍历结果:");
        list.stream().parallel().forEach(o->System.out.println(o.getName()));

运行结果:

顺序流的遍历结果:
hahah
wwww
sans
并行流的遍历结果:
wwww
sans
hahah

总结:forEach 方法的参数也是个 Consumer 类型,所以是对 stream 中每个元素进行处理,没有返回值,比如上述代码中输出每个元素的 name 。 forEach 遍历 stream 中的元素顺序问题:如果 stream 是一个顺序流,那么 forEach 遍历的结果就是顺序的,如果 stream 是一个并行流,那么 forEach 遍历的结果就未必是顺序的。为了解决这个“未必”问题,引出了 forEachOrdered 方法。

10. forEachOrdered 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        System.out.println("并行流的遍历结果:");
        list.stream().parallel().forEachOrdered(o->System.out.println(o.getName()));

运行结果:

并行流的遍历结果:
hahah
wwww
sans

总结:不管是顺序流,还是并行流,forEachOrdered 方法都能够保证遍历是顺序的。

11. toArray 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        Object[] haha = list.stream().toArray();
        Person[] hehe = list.stream().toArray(Person[]::new);
        System.out.println(hehe.length);

运行结果:

3

总结:toArray 方法就是将 stream 中的元素形成一个数组,不给出 toArray 方法的参数,就会返回 Object 数组。具体看上述代码例子。

12. count 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        long count = list.stream().count();
        System.out.println(count);

运行结果:

3

总结:count 方法用于统计 stream 中元素的总个数。

13. max min 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        Optional<Person> max = list.stream().max(Comparator.comparing(Person::getAge));
        Optional<Person> max2 = list.stream().max((o1, o2)-> {
            if(o1.getName().length() > o2.getName().length()) return 1;
            else return 0;
        });
        if(max.isPresent()) System.out.println("年龄最大的:" + max.get().getName());
        if(max2.isPresent()) System.out.println("名字最长的:" + max2.get().getName());

运行结果:

年龄最大的:wwww
名字最长的:hahah

总结:max 的参数就是一个 Comparator 参数,定义一个比较大小的规则,然后得出 stream 中最大的。 min 同理。

14. findFirst findAny 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        Optional<Person> p = list.stream().findFirst();
        if(p.isPresent()) System.out.println(p.get().getName());
        Optional<Person> p2 = list.stream().findAny();
        if(p2.isPresent()) System.out.println(p2.get().getName());

运行结果:

hahah
hahah

总结:findFirst 用于得到 stream 中的第一个元素,不管是顺序流 还是 并行流。 而 findAny 则不同,如果是顺序流的话,一般是返回第一个元素,如果是并行流的话,那就是随机的。

15. anyMatch allMatch noneMatch 方法

        List<Person> list = new LinkedList<>();
        Person one = new Person(12, "hahah");
        Person two = new Person(15, "wwww");
        Person three = new Person(13, "sans");
        list.add(one);
        list.add(two);
        list.add(three);
        boolean result = list.stream().allMatch(o->o.getAge() > 13);
        boolean result2 = list.stream().anyMatch(o->o.getAge() > 13);
        boolean result3 = list.stream().noneMatch(o->o.getAge() > 13);
        System.out.println(result + "," + result2 + "," + result3);

运行结果:

false,true,false

总结:anyMatch 方法用于条件查询 stream 中的元素,如果存在元素满足指定的条件,则返回true,否则返回 false;allMatch 方法同理,必须要求 stream 中所有元素都满足指定条件,才返回 true ,否则返回 false。noneMatch 方法则与 allMatch 相反,必须要求 stream 中的所有元素都不满足指定条件,才返回 true。

16. reduce 方法

等我更新。。。。。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值