Java8学习--Stream&Collectors

java8关于stream操作api总结:

一、先介绍几个名词:

1、中间操作:

中间操作会产生另一个流。因此中间操作可以用来创建执行一系列动作的管道。一个特别需要注意的点是:中间操作不是立即发生的。相反,当在中间操作创建的新流上执行完终端操作后,中间操作指定的操作才会发生

2、终止操作:

会消费流,这种操作会产生一个结果的,如果一个流被消费过了,那它就不能被重用的。

3、"中间操作"的状态:

(1)无状态操作:在处理流中的每个元素时,与其他元素无关

(2)有状态操作:在处理流中的每个元素时,依赖其他元素(eg:min,max之类的)

4、缩减操作:

把一个流经过一个操作,输出一个值。eg:min、max,或者典型的reduce操作

5、for&forEach:

我们在访问一个数组元素的时候,最快的方式肯定是通过索引去访问的吧,而for循环遍历的时候就是通过下标进行的,所以效率那是相当的高,但是当我们的数据结构不是数组的时候,比如是链表的时候,可想而知,for循环的效率是有多低,但是forEach底层采用的是迭代器的方式,他对数据结构是没有要求的,不管上层的数据结构是什么,他都能保证高效地执行!因此我的最终答案:如果数据结构是ArrayList这种数据结构,那你可以采用for,但是你的数据结构如果是LinkList那你千万别再用for,应该果断采用forEach。

6、并行流:

调用parallel()就可以获取到一个并行流了,帮助我们多线程操作,提高效。需要注意的是,服务器需要多核支持

for循环,stream操作,并行stream操作的性能对比:

并行stream > for > stream : 数据量不大或者核数不够多的情况下,for比并行stream效率高

二、各种api:

1、中间操作——筛选
方法 描述
filter(Predicate p) 接收 Lambda , 从流中排除某些元素。
distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量。
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
2、中间操作——映射
方法 描述
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction ) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction ) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction ) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
3、中间操作——排序
方法 描述
sorted() 产生一个新流,其中按自然顺序排序
sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
4、终止操作——查找与匹配
方法 描述
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate ) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) stream API 使用内部迭代(默认做了外部迭代)
5、终止操作——归约
方法 描述
reduce(T identity, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T。<br />在第一个版本当中,identity是这样一个值,对于涉及identity和流中任意的累积操作,得到的结果就是元素自身,没有任何改变。比如,如果是加法,他就是0,如果是乘法他就是1。
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
@Test
public  void reduceParallel() {
    List<Integer> lists = new ArrayList<>();
    lists.add(1);
    lists.add(2);
    lists.add(3);

    //每个元素都放大两倍后,再求积
    Integer product = lists.parallelStream().reduce(1, (a, b) -> a *  (b * 2),
                                                    (a, b) -> a * b);
    System.out.println("product:" + product);//48

    Integer product2 = lists.parallelStream().reduce(1, (a, b) -> {
        System.out.println("第一个参数:" + Thread.currentThread().getName() +",a="+a+",b="+b);
        return a *  (b * 2);},
                                                     (a, b) -> {
                                                         System.out.println("第二个参数:" + Thread.currentThread().getName()+",c="+a+",d="+b);
                                                         return  a * b;
                                                     });
    System.out.println("product2:" + product);//48
}
6、终止操作——收集,注意这里返回的都是一个收集器Collector
方法 描述
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

三、关于各种收集器:

1、均值——averaging
方法 描述
averagingInt(ToIntFunction<? super T> mapper) 接收一个函数作为参数,求该函数的返回值(int)的均值
averagingLong(ToLongFunction<? super T> mapper) 接收一个函数作为参数,求该函数的返回值(long)的均值
averagingDouble(ToDoubleFunction<? super T> mapper) 接收一个函数作为参数,求该函数的返回值(double)的均值
@Test
public void testAveraging() {
    List<Integer> lists = new ArrayList<>();
    lists.add(1);
    lists.add(2);
    lists.add(3);
    Double average = lists.stream().collect(Collectors.averagingInt(item -> item));
    System.out.println(average);
}
2、统计——元素个数:counting
方法 描述
counting() 统计输入元素的个数
@Test
public void testCounting() {
    List<Integer> lists = new ArrayList<>();
    lists.add(1);
    lists.add(2);
    lists.add(3);
    Long count = lists.stream().collect(Collectors.counting());
    System.out.println(count);
}
3、最值——maxBy、minBy
方法 描述
minBy(Comparator<? super T> comparator) 接收一个比较器,返回最小值
maxBy(Comparator<? super T> comparator) 接收一个比较器,返回最大值
@Test
public void testMax() {
    List<Integer> lists = new ArrayList<>();
    lists.add(1);
    lists.add(2);
    lists.add(3);
    Optional<Integer> max = lists.stream().collect(Collectors.maxBy(Integer::compare));
    if (max.isPresent()){
        System.out.println(max.get());
    }
}
4、求和——summing、summarizing
方法 描述
summingInt(ToIntFunction<? super T> mapper) 可以计算输入元素的总和
summingLong(ToLongFunction<? super T> mapper) 可以计算输入元素的总和
summingDouble(ToDoubleFunction<? super T> mapper) 可以计算输入元素的总和
summarizingInt(ToIntFunction<? super T> mapper) 可以计算输入元素的总和,平均值,数量,最值
summarizingLong(ToLongFunction<? super T> mapper) 可以计算输入元素的总和,平均值,数量,最值
summarizingDouble(ToDoubleFunction<? super T> mapper) 可以计算输入元素的总和,平均值,数量,最值
@Test
public void testSummary() {
    List<Integer> lists = new ArrayList<>();
    lists.add(1);
    lists.add(2);
    lists.add(3);
    Integer sum = lists.stream().collect(Collectors.summingInt((item) -> item));
    System.out.println(sum);

    IntSummaryStatistics summary = lists.stream().collect(Collectors.summarizingInt((item) -> item));
    System.out.println(summary.getSum());
    System.out.println(summary.getAverage());
    System.out.println(summary.getCount());
    System.out.println(summary.getMax());
    System.out.println(summary.getMin());
}
5、分组——groupingBy,groupingByConcurrent
方法 描述
groupingBy(Function<? super T, ? extends K> classifier) classifier提供结果Map(HashMap)的键
groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) downstream提供结果Map和值
groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) mapFactory指定结果Map的类型
groupingByConcurrent(Function<? super T, ? extends K> classifier) classifier提供结果Map(HashMap)的键,将元素整理成ConcurrentMap
groupingByConcurrent(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) downstream提供结果Map和值,将元素整理成ConcurrentMap
groupingByConcurrent(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory,Collector<? super T, A, D> downstream) mapFactory指定结果Map的类型,将元素整理成ConcurrentMap
@Data
@Builder
static class Student{
    private Long id;
    private String name;
    private Long classId;
    private Integer age;
}

@Test
public void testGroup() {
    List<Student> list = new ArrayList<>();
    Student zhangSan = Student.builder().id(1L).name("张三").classId(1L).age(20).build();
    Student liSi = Student.builder().id(2L).name("李四").classId(1L).age(21).build();
    Student wangWu = Student.builder().id(3L).name("王五").classId(2L).age(21).build();
    Student zhaoLiu = Student.builder().id(4L).name("赵六").classId(1L).age(21).build();
    list.add(liSi);
    list.add(zhangSan);
    list.add(wangWu);
    list.add(zhaoLiu);

    //根据classId分组
    Map<Long, List<Student>> map1 = list.stream().collect(Collectors.groupingBy(Student::getClassId));
    System.out.println("map1:\n" + map1);
    //统计各个classId的人数
    Map<Long, Long> map2 = list.stream().collect(Collectors.groupingBy(Student::getClassId,
                                                                       Collectors.counting()));
    System.out.println("map2:\n" + map2);
    //统计各个classId的人数
    Map<Long, Long> map3 = list.stream().collect(Collectors.groupingBy(Student::getClassId,
                                                                       HashMap::new,
                                                                       Collectors.counting()));
    System.out.println("map3:\n" + map3);
}
6、分区——partitioningBy
方法 描述
partitioningBy(Predicate<? super T> predicate) 该操作将输入元素分为两类(即键是true和false的Map),predicate提供分区依据
partitioningBy(Predicate<? super T> predicate,Collector<? super T, A, D> downstream) 该操作将输入元素分为两类(即键是true和false的Map),downstream提供结果Map的值
@Test
public void testPartitioning() {
    List<Student> list = new ArrayList<>();
    Student zhangSan = Student.builder().id(1L).name("张三").classId(1L).age(20).build();
    Student liSi = Student.builder().id(2L).name("李四").classId(1L).age(21).build();
    Student wangWu = Student.builder().id(3L).name("王五").classId(2L).age(21).build();
    Student zhaoLiu = Student.builder().id(4L).name("赵六").classId(1L).age(21).build();
    list.add(liSi);
    list.add(zhangSan);
    list.add(wangWu);
    list.add(zhaoLiu);

    Map<Boolean, List<Student>> map = list.stream().collect(Collectors.partitioningBy((item -> item.getAge() > 20)));
    System.out.println("map:\n" + map);

    Map<Boolean, Long> map1 = list.stream().collect(Collectors.partitioningBy((item -> item.getAge() > 20), 
                                                                              Collectors.counting()));
    System.out.println("map1:\n" + map1);//{false=1, true=3}
}
7、Collector之后再操作——collectingAndThen
方法 描述
collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) downstream(Collector类型)和finisher(Function类型),在调用downstream之后,将调用结果值作为finisher的传入值,再调用finisher。
@Test
public void testCollectingAndThen() {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    String result = list.stream().collect(Collectors.collectingAndThen(Collectors.counting(),
                                                                       (ret) -> "统计结果为=" + ret));
    System.out.println(result);
}
8、拼接——joining
方法 描述
joining() 将输入元素(字符串类型)拼接成字符串,拼接输入元素
joining(CharSequence delimiter) 将输入元素(字符串类型)拼接成字符串,将delimiter作为分隔符
joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) 将输入元素(字符串类型)拼接成字符串,将prefix作为前缀,suffix作为后缀
@Test
public void testCollectingAndThen() {
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    String result = list.stream().collect(Collectors.joining());
    System.out.println(result);//abc
    String result2 = list.stream().collect(Collectors.joining("-"));
    System.out.println(result2);//a-b-c
    String result3 = list.stream().collect(Collectors.joining(",", "[", "]"));
    System.out.println(result3);//[a,b,c]
}
9、mapping
方法 描述
mapping(Function<? super T, ? extends U> mapper,Collector<? super U, A, R> downstream) mapper(Function类型)和downstream(Collector类型),在调用mapper之后,将调用结果的返回值作为downstream的输入元素,再调用downstream
@Test
public void testMapping() {
    List<Student> list = new ArrayList<>();
    Student zhangSan = Student.builder().id(1L).name("张三").classId(1L).age(20).build();
    Student liSi = Student.builder().id(2L).name("李四").classId(1L).age(21).build();
    Student wangWu = Student.builder().id(3L).name("王五").classId(2L).age(21).build();
    Student zhaoLiu = Student.builder().id(4L).name("赵六").classId(1L).age(21).build();
    list.add(liSi);
    list.add(zhangSan);
    list.add(wangWu);
    list.add(zhaoLiu);
    String result = list.stream().collect(Collectors.mapping(Student::getName, Collectors.joining(",", "[", "]")));
    System.out.println(result);//[李四,张三,王五,赵六]
}
10、缩减——reducing
方法 描述
reducing(BinaryOperator<T> op) 对输入元素执行缩减操作,对输入的元素应用op操作
reducing(T identity, BinaryOperator<T> op) 对输入元素执行缩减操作,提供初始值identity
reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op) 对输入元素执行缩减操作,在对元素进行op操作之前,先进行mapper操作
@Test
public void testReducing() {
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    //求和,返回Optional
    Optional<Integer> result = list.stream().collect(Collectors.reducing((a, b) -> a + b));
    if (result.isPresent()){
        System.out.println(result.get());//6
    }
    //求和,返回Integer
    Integer result2 = list.stream().collect(Collectors.reducing(0, (a, b) -> a + b));
    System.out.println(result2);//6
    //每个元素先平方,再求和
    Integer result3 = list.stream().collect(Collectors.reducing(0, item -> item * item, (a, b) -> a + b));
    System.out.println(result3);//14
}
11、集合转化——toCollection、toList与toSet
方法 描述
toCollection(Supplier<C> collectionFactory) 将输入元素整理成集合,collectionFactory可以指定结果集合的类型,将输入元素整理成集合
toList() 将输入元素整理成集合,将输入元素整理成ArrayList
toSet() 将输入元素整理成集合,将输入元素整理成HashSet
@Test
public void testToCollection() {
    List<Student> list = new ArrayList<>();
    Student zhangSan = Student.builder().id(1L).name("张三").classId(1L).age(20).build();
    Student liSi = Student.builder().id(2L).name("李四").classId(1L).age(21).build();
    Student wangWu = Student.builder().id(3L).name("王五").classId(2L).age(21).build();
    Student zhaoLiu = Student.builder().id(4L).name("赵六").classId(1L).age(21).build();
    list.add(liSi);
    list.add(zhangSan);
    list.add(wangWu);
    list.add(zhaoLiu);
    List<Student> toList = list.stream().filter(item -> item.getClassId().equals(1L)).collect(Collectors.toList());
    Set<Student> toSet = list.stream().filter(item -> item.getClassId().equals(1L)).collect(Collectors.toSet());
    ArrayList<Student> toCollection = list.stream().filter(item -> item.getClassId().equals(1L)).collect(Collectors.toCollection(ArrayList::new));
}
12、Map转化——toMap与toConcurrentMap
方法 描述
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) 将输入元素整理成Map,keyMapper和valueMapper分别提供结果Map的键和值
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction) 将输入元素整理成Map,mergeFunction对键相同的值进行累积
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) 将输入元素整理成Map,mapSupplier可以指定结果的Map类型
@Test
public void testToMap() {
    List<Student> list = new ArrayList<>();
    Student zhangSan = Student.builder().id(1L).name("张三").classId(1L).age(20).build();
    Student liSi = Student.builder().id(2L).name("李四").classId(1L).age(21).build();
    Student wangWu = Student.builder().id(3L).name("王五").classId(2L).age(21).build();
    Student zhaoLiu = Student.builder().id(4L).name("赵六").classId(1L).age(21).build();
    list.add(liSi);
    list.add(zhangSan);
    list.add(wangWu);
    list.add(zhaoLiu);

    //转为map,存在相同的key的话会抛异常
    Map<Long, String> map = list.stream().collect(Collectors.toMap(item -> item.getId(), 
                                                                   item -> item.getName()));
    System.out.println(map);//{1=张三, 2=李四, 3=王五, 4=赵六}

    //转化为map,若key相等,取后一个
    Student zhaoLiu2 = Student.builder().id(4L).name("赵六2").classId(1L).age(21).build();
    list.add(zhaoLiu2);
    Map<Long, String> map1 = list.stream().collect(Collectors.toMap(item -> item.getId(), 
                                                                    item -> item.getName(), 
                                                                    (a, b) -> b));
    System.out.println(map1);//{1=张三, 2=李四, 3=王五, 4=赵六2}

    //指定返回的map类型为TreeMap
    TreeMap<Long, String> map3 = list.stream().collect(Collectors.toMap(item -> item.getId(), 
                                                                        item -> item.getName(), 
                                                                        (a, b) -> b, 
                                                                        TreeMap::new));
    System.out.println(map3);//{1=张三, 2=李四, 3=王五, 4=赵六2}

}

参考文章:https://www.jianshu.com/p/ac2bcf2f9d48
参考文章:https://blog.csdn.net/xiliunian/article/details/88773718
参考文章:https://blog.csdn.net/weixin_44187730/article/details/93737517

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

推荐阅读更多精彩内容

  • Java8实战之Stream 前言 在前面一个小节中,我们已经学习了行为参数化以及Lambda表达式,通过Lamb...
    颜洛滨阅读 1,041评论 0 2
  • 本笔记来自 计算机程序的思维逻辑 系列文章 Lambda表达式 Lambda表达式 语法 匿名函数,由 -> 分隔...
    码匠阅读 475评论 0 6
  • Int Double Long 设置特定的stream类型, 提高性能,增加特定的函数 无存储。stream不是一...
    patrick002阅读 1,274评论 0 0
  • Java流库(java.util.stream) 流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图...
    thorhill阅读 4,840评论 0 4
  • 1 函数式接口和lambda表达式 函数式接口函数式接口就是有且只有一个抽象方法的接口,需要注意的是,如果方法覆盖...
    曾猪666阅读 982评论 0 0