zoukankan      html  css  js  c++  java
  • java8--List转为Map、分组、过滤、求和等操作----代码示例

    Java 8 函数式编程风格

    Java 迄今为止最令人激动的特征。这些新的语言特征允许采用函数式风格来进行编码,我们可以用这些特性完成许多有趣的功能。这些特性如此有趣以至于被认为是不合理的.他们说会影响计算速度,但是虽然是真的,但是存在皆合理.

    所以我们摒弃缺点,研究优点.

    演练

    今天的新闻联播播出的主要内容有:list转map,list使用lambda求和,等聚合运算,映射分类,分组,排序,归约等算法示例 你们就静鸡鸡的看吧

    ------还演练了parallelStream并行流          [林凌你个鸭蛋 要是进来不点赞我就....]
      1 package test;
      2 
      3 /**
      4  * @Title: Dog.java
      5  * @Description: Stream的演练
      6  * @author LiJing
      7  * @date 2018/12/3 14:13
      8  * @version 1.0.0
      9  * @implNote stream的特性 我们首先列出stream的如下三点特性,在之后我们会对照着详细说明
     10  * 1.stream不存储数据
     11  * 2.stream不改变源数据
     12  * 3.stream的延迟执行特性
     13  * 通常我们在数组或集合的基础上创建stream,stream不会专门存储数据,对stream的操作也不会影响到创建它的数组和集合,
     14  * 对于stream的聚合、消费或收集操作只能进行一次,再次操作会报错
     15  */
     16 
     17 import com.google.common.collect.Lists;
     18 import lombok.AllArgsConstructor;
     19 import lombok.Data;
     20 import lombok.NoArgsConstructor;
     21 
     22 import java.text.Collator;
     23 import java.util.*;
     24 import java.util.function.Function;
     25 import java.util.stream.Collectors;
     26 import java.util.stream.Stream;
     27 
     28 @Data
     29 @NoArgsConstructor
     30 @AllArgsConstructor
     31 public class Dog {
     32     private Long id;//编号
     33     private String name;//名字
     34     private Integer age;//年龄
     35     private String team;//分队
     36 
     37     public static void main(String[] args) {
     38         List<Dog> list = Lists.newArrayList();
     39         list.add(new Dog(5L, "林凌", 2, "M"));
     40         list.add(new Dog(1L, "豆豆", 5, "F"));
     41         list.add(new Dog(2L, "苗苗", 3, "a"));
     42         list.add(new Dog(4L, "糖糖", 3, "A"));
     43         list.add(new Dog(6L, "文文", 3, "G"));
     44         list.add(new Dog(3L, "晴晴", 4, "g"));
     45 
     46         //提取组别号 转换大写  这里可以看到 一个 Stream 只可以使用一次 map映射有很多方法
     47         List<String> teamList = list.stream().map(Dog::getTeam).collect(Collectors.toList());
     48         List<String> listCase = teamList.stream().map(String::toUpperCase).collect(Collectors.toList());
     49 
     50         /**list操作*/
     51         //1.获取年龄最大/最小的狗
     52         Dog dog2MaxAge = list.stream().max((d1, d2) -> d1.getAge().compareTo(d2.getAge())).orElse(new Dog());
     53         Dog dog1MaxAge = list.stream().min(Comparator.comparing(Dog::getAge)).get();
     54 
     55         //2.静态方法 单一操作时候可以简化为如下:
     56         list.stream().forEach(x -> System.out.println(x));
     57         list.forEach(System.out::println);
     58 
     59         //3.排序 根据年龄排序 默认升序
     60         List<Dog> sorted1AgeList = list.stream().sorted(Comparator.comparing(Dog::getAge)).collect(Collectors.toList());
     61         //同等按照id再来排序,混合排序 复合排序
     62         List<Dog> sorted2AgeList = list.stream().sorted(Comparator.comparing(Dog::getAge).thenComparing(Dog::getId)).collect(Collectors.toList());
     63         //降序排列
     64         List<Dog> sorted3AgeList = list.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge()).collect(Collectors.toList());
     65         //反转 倒序,逆序处理
     66         List<Dog> sorted4AgeList = list.stream().sorted(Comparator.comparing(Dog::getAge).reversed()).collect(Collectors.toList());
     67 
     68         //4.根据组队号 自然排序
     69         List<Dog> sortedTeamList = list.stream().sorted(Comparator.comparing(Dog::getTeam)).collect(Collectors.toList());
     70         //5.根据名字的字母  Collator 类执行区分语言环境的 String 比较
     71         List<Dog> sortedNameList = list.stream().sorted((o1, o2) -> Collator.getInstance(Locale.CHINESE).compare(o1.getName(), o2.getName())).collect(Collectors.toList());
     72         //6.去重 去掉重复的结果
     73         List<Dog> distinctList = list.stream().distinct().collect(Collectors.toList());
     74         //7.截取 截取流的前N个元素
     75         List<Dog> limitList = list.stream().limit(3).collect(Collectors.toList());
     76         //8.跳过流的前n个元素
     77         List<Dog> skipList = list.stream().skip(4).collect(Collectors.toList());
     78         //9.将小流合并成一个大流
     79         List<String> listLine = new ArrayList<String>();
     80         listLine.add("I am a boy");
     81         listLine.add("I love the girl");
     82         listLine.add("But the girl loves another girl");
     83 
     84         //按空格分词 分完词之后,每个元素变成了一个String[]数组
     85         Stream<String[]> stream = listLine.stream().map(line -> line.split(" "));
     86         //将每个String[]变成流
     87         Stream<Stream<String>> streamStream = listLine.stream().map(line -> line.split(" ")).map(Arrays::stream);
     88         //此时一个大流里面包含了一个个小流,我们需要将这些小流合并成一个流
     89         Stream<String> stringStream = listLine.stream().map(line -> line.split(" ")).flatMap(Arrays::stream);
     90 
     91         //找出id>3的 按照年龄排序 输出他们的名字
     92         List<String> studentList = list.stream()
     93                 .filter(x -> x.getId() > 3)
     94                 .sorted(Comparator.comparing(Dog::getAge).reversed())
     95                 .map(Dog::getName)
     96                 .collect(Collectors.toList());
     97         System.out.println(studentList);
     98 
     99 
    100         /**map操作*/
    101         //1.转为Map 以编号:key 以名字为value  用::,和x->都可以 推荐::
    102         Map<Long, String> map1 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x.getName()));
    103 
    104         //2.转为Map 以编号:key 以对象自身为value  用x->x,和Function.identity() 推荐::Function.identity()
    105         Map<Long, Dog> map2 = list.stream().collect(Collectors.toMap(Dog::getId, x -> x));
    106         Map<Long, Dog> map3 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity()));
    107 
    108         //3.重复key的情况,这时候流的处理会抛出个异常:Java.lang.IllegalStateException:Duplicate key。
    109         //这时候就要在toMap方法中指定当key冲突时key的选择。(这里是选择第二个key覆盖第一个key)
    110         Map<Long, Dog> map4 = list.stream().collect(Collectors.toMap(Dog::getId, Function.identity(), (oldKey, newKey) -> newKey));
    111 
    112         //4.根据 年龄or组队 分组用groupingBy 可以分组成多个列表
    113         Map<Integer, List<Dog>> map5 = list.stream().collect(Collectors.groupingBy(Dog::getAge));
    114         Map<String, List<Dog>> map6 = list.stream().collect(Collectors.groupingBy(Dog::getName));
    115 
    116         //5.partitioningBy可以理解为特殊的groupingBy,key值为true和false,当然此时方法中的参数为一个判断语句(用于判断的函数式接口)
    117         Map<Boolean, List<Dog>> map7 = list.stream().collect(Collectors.partitioningBy(o -> o.getAge() < 3));
    118 
    119         //遍历小map
    120         map6.forEach((k, v) -> System.out.println(k + v));
    121 
    122 
    123         //归约 获取年龄最大的狗狗
    124         Dog dog = list.stream().reduce((p1, p2) -> {
    125             System.out.println("p1===" + p1);
    126             System.out.println("p2===" + p2);
    127             return p1.getAge() > p2.getAge() ? p1 : p2;
    128         }).get();
    129         System.out.println(dog);
    130 
    131         //归约 求和 计算所有狗狗的年龄总和
    132         //reduce的第一个参数表示初试值为0;
    133         //求和 那么可以使用Integer提供了sum函数代替自定义的Lambda表达式
    134         int age2Total = list.stream().mapToInt(Dog::getAge).reduce(0, Integer::sum);
    135 
    136         /**聚合运算*/
    137         //求和
    138         long sum = list.stream().mapToLong(Dog::getId).sum();
    139         //均值
    140         double average = list.stream().mapToInt(Dog::getAge).average().getAsDouble();
    141         //统计
    142         long count = list.stream().count();
    143         long count1 = list.stream().map(Dog::getId).count();
    144         long count2 = list.stream().mapToInt(Dog::getAge).count();
    145 
    146         /**匹配:*/
    147         //如果流为空,则返回false并且不评估谓词 ==>检查所有的名字是否是以 '文'结尾的  显然不是 [少一个都不行,全检性]  看电影去:<一个都不能少>
    148         boolean b1 = list.stream().allMatch(x -> x.getName().endsWith("文"));//false
    149         //如果流为空,则返回false并且不评估谓词 ==>检查是否有在A组的小狗  [存在性]
    150         boolean b2 = list.stream().anyMatch(x -> "A".compareToIgnoreCase(x.getTeam()) < 0);//true
    151         //如果流为空,则返回false并且不评估谓词 ==>检查是否有任意小狗 年龄大于3岁  [存在性]
    152         boolean b3 = list.stream().anyMatch(x -> x.getAge() < 3);//true
    153         // list = Collections.EMPTY_LIST;
    154         //如果流为空,则返回true并且不评估谓词 ==> allMatch相反,检查有么狗的年龄大于100岁的   [有一个都不行,全检性]
    155         boolean b4 = list.stream().noneMatch(x -> x.getAge() > 100);//true
    156 
    157         //findAny list即stream是有顺序的 findAny能够从流中随便选一个元素出来
    158         Optional<Dog> optionalDog = list.stream().findAny();//isPresent表示是否存在
    159         System.out.println(optionalDog.isPresent());
    160 
    161         //再看 其实就是 b6就是b7
    162         boolean b6 = list.stream().filter(x -> x.getAge() < 3).findAny().isPresent();//true
    163         //过滤
    164         List<Dog> filterList1 = list.stream().filter(x -> x.getAge() < 3).collect(Collectors.toList());
    165         boolean b7 = list.stream().anyMatch(x -> x.getAge() < 3);//true 返回在流遍历过程是否遇到过的年龄大于3的狗
    166 
    167         //与guava的getFirst方法类似,Stream.findFirst将返回给定流的第一个元素,如果流为空,则返回空Optional。如果流没有遭遇顺序,则可以返回任何元素
    168         Optional<Dog> first = list.stream().findFirst();
    169 
    170 
    171         /**
    172          * parallelStream是什么
    173          * parallelStream其实就是一个并行执行的流.它通过默认的ForkJoinPool,可能提高你的多线程任务的速度.
    174          * Stream具有平行处理能力,处理的过程会分而治之,也就是将一个大任务切分成多个小任务,这表示每个任务都是一个操作,因此像以下的程式片段
    175          *
    176          * 而可能是任意的顺序,就forEach()这个操作來讲,如果平行处理时,希望最后顺序是按照原来Stream的数据顺序,那可以调用forEachOrdered()
    177          * 如果forEachOrdered()中间有其他如filter()的中介操作,会试着平行化处理,然后最终forEachOrdered()会以原数据顺序处理,
    178          * 因此,使用forEachOrdered()这类的有序处理,可能会(或完全失去)失去平行化的一些优势,实际上中介操作亦有可能如此,例如sorted()方法
    179          *
    180          * parallelStream背后的男人:ForkJoinPool
    181          * ForkJoinPool主要用来使用分治法(Divide-and-Conquer Algorithm)来解决问题
    182          * 那么使用ThreadPoolExecutor或者ForkJoinPool,会有什么性能的差异呢?
    183          * 首先,使用ForkJoinPool能够使用数量有限的线程来完成非常多的具有父子关系的任务,比如使用4个线程来完成超过200万个任务。
    184          * 但是,使用ThreadPoolExecutor时,是不可能完成的,因为ThreadPoolExecutor中的Thread无法选择优先执行子任务,
    185          * 需要完成200万个具有父子关系的任务时,也需要200万个线程,显然这是不可行的。
    186          * */
    187         List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
    188         numbers.parallelStream().forEach(System.out::print);
    189         System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一样");
    190         numbers.stream().parallel().forEach(System.out::print);
    191         System.out.println("numbers.parallelStream()和numbers.stream().parallel()貌似一样");
    192         numbers.stream().parallel().forEachOrdered(System.out::print);
    193         System.out.println();
    194 
    195         Optional<Dog> any = list.parallelStream().findAny();
    196         System.out.println(any.isPresent());
    197 
    198         //并行流 此时的findAny哪一个都有可能返回
    199         Optional<Dog> any1 = list.stream().parallel().findAny();
    200         System.out.println(any1);
    201 
    202         //生成一亿条0-100之间的记录
    203         //感受:可见,对于串行流.distinct().sorted()方法对于运行时间没有影响,
    204         // 但是对于串行流,会使得运行时间大大增加,因此对于包含sorted、distinct()等与全局数据相关的操作,不推荐使用并行流。
    205         Random random = new Random();
    206         List<Integer> integerList = Stream.generate(() -> random.nextInt(100)).limit(100000000).collect(Collectors.toList());
    207         long begin1 = System.currentTimeMillis();
    208         integerList.stream().filter(x -> (x > 10)).filter(x -> x < 80).count();
    209         long end1 = System.currentTimeMillis();
    210         System.out.println(end1 - begin1);
    211         integerList.stream().parallel().filter(x -> (x > 10)).filter(x -> x < 80).count();
    212         long end2 = System.currentTimeMillis();
    213         System.out.println(end2 - end1);
    214 
    215         long begin1_ = System.currentTimeMillis();
    216         integerList.stream().filter(x -> (x > 10)).filter(x -> x < 80).distinct().sorted().count();
    217         long end1_ = System.currentTimeMillis();
    218         System.out.println(end1 - begin1);
    219         integerList.stream().parallel().filter(x -> (x > 10)).filter(x -> x < 80).distinct().sorted().count();
    220         long end2_ = System.currentTimeMillis();
    221         System.out.println(end2_ - end1_);
    222 
    223 
    224         //调用peek方法来瞧瞧并行流和串行流的执行顺序,peek方法顾名思义,就是偷窥流内的数据,
    225         // peek方法声明为Stream<T> peek(Consumer<? super T> action);加入打印程序可以观察到通过流内数据,见如下代码
    226 
    227         new Dog().peek();//串行流          线程:main:->peek2->7
    228         new Dog().testPeekPal();//并行流   线程:ForkJoinPool.commonPool-worker-1:->peek1->4
    229 
    230 
    231         /**总结:
    232          *我们将stream.filter(x -> x > 5).filter(x -> x < 8).forEach(System.out::println)的过程想象成 如图-1 的管道,我们在管道上加入的peek相当于一个阀门,透过这个阀门查看流经的数据,
    233          *1)当我们使用顺序流时,数据按照源数据的顺序依次通过管道,当一个数据被filter过滤,或者经过整个管道而输出后,第二个数据才会开始重复这一过程
    234          *2)当我们使用并行流时,系统除了主线程外启动了七个线程(我的电脑是4核八线程)来执行处理任务,因此执行是无序的,但同一个线程内处理的数据是按顺序进行的。
    235          * */
    236     }
    237 
    238 
    239     public void peek() {
    240         //生成1-10的int 流
    241         Stream<Integer> integerStream = Stream.iterate(1, x -> x + 1).limit(10);
    242         integerStream.peek(this::peek1).filter(x -> x > 5)
    243                 .peek(this::peek2).filter(x -> x < 8)
    244                 .peek(this::peek3)
    245                 .forEach(System.out::println);
    246     }
    247 
    248     public void testPeekPal() {
    249         Stream<Integer> stream = Stream.iterate(1, x -> x + 1).limit(10).parallel();
    250         stream.peek(this::peek1).filter(x -> x > 5)
    251                 .peek(this::peek2).filter(x -> x < 8)
    252                 .peek(this::peek3)
    253                 .forEach(System.out::println);
    254     }
    255 
    256     public void peek1(int x) {
    257         System.out.println(Thread.currentThread().getName() + ":->peek1->" + x);
    258     }
    259 
    260     public void peek2(int x) {
    261         System.out.println(Thread.currentThread().getName() + ":->peek2->" + x);
    262     }
    263 
    264     public void peek3(int x) {
    265         System.out.println(Thread.currentThread().getName() + ":->final result->" + x);
    266     }
    267 
    268 
    269 }

                       

     图1--如上

    图2--如下

     Collectors 类的静态工厂方法:

    工厂方法返回类型作用
    toList List<T> 把流中所有项目收集到一个 List
    toSet Set<T> 把流中所有项目收集到一个 Set,删除重复项
    toCollection Collection<T> 把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new)
    counting Long 计算流中元素的个数
    sumInt Integer 对流中项目的一个整数属性求和
    averagingInt Double 计算流中项目 Integer 属性的平均值
    summarizingInt IntSummaryStatistics 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值
    joining String 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", "))
    maxBy Optional<T> 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty()
    minBy Optional<T> 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty()
    reducing 归约操作产生的类型 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
    collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
    groupingBy Map<K, List<T>> 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键
    partitioningBy Map<Boolean,List<T>> 根据对流中每个项目应用谓词的结果来对项目进行分区
    1 List<Matchs> matchsList = new ArrayList();
    2 
    3 Map<String,List<Matchs>> MatchsListMap=matchsList.stream()
    4 .collect(Collectors.groupingBy(Matchs::getMatchDate)); 

    此时MatchsListMap的排序规则是根据MatchDate降序的(默认),也就是说map中0下标的key是最大的MatchDate值,那么如果需要根据MatchDate升序该怎么办呢?

    TreeMap<String, List<Matchs>> matchsListMap = matchsList.stream()
    .collect(Collectors.groupingBy(Matchs::getMatchDate,TreeMap::new,Collectors.toList()));

    此时返回的为TreeMap类型数据,TreeMap默认为按照key升序,descendingMap()降序输出

    一切没有栗子的讲解都是耍流氓

  • 相关阅读:
    storm学习笔记
    Hbase学习笔记
    Hadoop实战项目之网站数据点击流分析(转载分析)
    Hive实战之学生选课
    Hive实战之求月销售额和累计销售额
    Hive实战之每年最高温度+时间
    Hive实战之学生课程成绩
    网易-C++开发实习生-业务初面和复面(视频)-20211028
    2021粤港澳大湾区智能网络与通信系统论坛-1026~1027-线上
    jupyter notebook
  • 原文地址:https://www.cnblogs.com/xxmyz/p/10060002.html
Copyright © 2011-2022 走看看