当前位置 : 主页 > 编程语言 > java >

Java8新特性 StreamAPI实例详解

来源:互联网 收集:自由互联 发布时间:2023-01-30
目录 Stream结果收集 结果收集到集合中 结果收集到数组中 对流中的数据做聚合计算 对流中数据做分组操作 对流中的数据做分区操作 对流中的数据做拼接 并行的Stream流 串行的Stream流
目录
  • Stream结果收集
    • 结果收集到集合中
    • 结果收集到数组中
    • 对流中的数据做聚合计算
    • 对流中数据做分组操作
    • 对流中的数据做分区操作
    • 对流中的数据做拼接
  • 并行的Stream流
    • 串行的Stream流
    • 并行流
      • 获取并行流
      • 并行流操作
    • 并行流和串行流对比
      • 线程安全问题

      Stream结果收集

      面试官:说说你常用的StreamAPI。

      结果收集到集合中

          public static void main(String[] args){
              // Stream<String> stream = Stream.of("aa", "bb", "cc");
              List<String> list = Stream.of("aa", "bb", "cc","aa")
                      .collect(Collectors.toList());
              System.out.println(list);
              // 收集到 Set集合中
              Set<String> set = Stream.of("aa", "bb", "cc", "aa")
                      .collect(Collectors.toSet());
              System.out.println(set);
              // 如果需要获取的类型为具体的实现,比如:ArrayList HashSet
              ArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa")
                      //.collect(Collectors.toCollection(() -> new ArrayList<>()));
                      .collect(Collectors.toCollection(ArrayList::new));
              System.out.println(arrayList);
              HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa")
                      .collect(Collectors.toCollection(HashSet::new));
              System.out.println(hashSet);
          }
      

      输出:

      [aa, bb, cc, aa]
      [aa, bb, cc]
      [aa, bb, cc, aa]
      [aa, bb, cc]

      结果收集到数组中

      Stream中提供了toArray方法来将结果放到一个数组中,返回值类型是Object[],如果我们要指定返回的类型,那么可以使用另一个重载的toArray(IntFunction f)方法。

          public static void main(String[] args){
              Object[] objects = Stream.of("aa", "bb", "cc", "aa")
                      .toArray(); // 返回的数组中的元素是 Object类型
              System.out.println(Arrays.toString(objects));
              // 如果我们需要指定返回的数组中的元素类型
              String[] strings = Stream.of("aa", "bb", "cc", "aa")
                      .toArray(String[]::new);
              System.out.println(Arrays.toString(strings));
          }
      

      对流中的数据做聚合计算

      当我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作,比如获得最大值,最小值,求和,平均值,统计数量。

          public static void main(String[] args) {
              // 获取年龄的最大值
              Optional<Person> maxAge = Stream.of(
                      new Person("张三", 18)
                      , new Person("李四", 22)
                      , new Person("张三", 13)
                      , new Person("王五", 15)
                      , new Person("张三", 19)
              ).collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
              System.out.println("最大年龄:" + maxAge.get());
              // 获取年龄的最小值
              Optional<Person> minAge = Stream.of(
                      new Person("张三", 18)
                      , new Person("李四", 22)
                      , new Person("张三", 13)
                      , new Person("王五", 15)
                      , new Person("张三", 19)
              ).collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
              System.out.println("最新年龄:" + minAge.get());
              // 求所有人的年龄之和
              Integer sumAge = Stream.of(
                      new Person("张三", 18)
                      , new Person("李四", 22)
                      , new Person("张三", 13)
                      , new Person("王五", 15)
                      , new Person("张三", 19)
              )
                      //.collect(Collectors.summingInt(s -> s.getAge()))
                      .collect(Collectors.summingInt(Person::getAge))
                      ;
              System.out.println("年龄总和:" + sumAge);
              // 年龄的平均值
              Double avgAge = Stream.of(
                      new Person("张三", 18)
                      , new Person("李四", 22)
                      , new Person("张三", 13)
                      , new Person("王五", 15)
                      , new Person("张三", 19)
              ).collect(Collectors.averagingInt(Person::getAge));
              System.out.println("年龄的平均值:" + avgAge);
              // 统计数量
              Long count = Stream.of(
                      new Person("张三", 18)
                      , new Person("李四", 22)
                      , new Person("张三", 13)
                      , new Person("王五", 15)
                      , new Person("张三", 19)
              ).filter(p->p.getAge() > 18)
                      .collect(Collectors.counting());
              System.out.println("满足条件的记录数:" + count);
          }
      

      对流中数据做分组操作

      当我们使用Stream流处理数据后,可以根据某个属性将数据分组。

          public static void main(String[] args){
              // 根据账号对数据进行分组
              Map<String, List<Person>> map1 = Stream.of(
                      new Person("张三", 18, 175)
                      , new Person("李四", 22, 177)
                      , new Person("张三", 14, 165)
                      , new Person("李四", 15, 166)
                      , new Person("张三", 19, 182)
              ).collect(Collectors.groupingBy(Person::getName));
              map1.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v));
              System.out.println("-----------");
              // 根据年龄分组 如果大于等于18 成年否则未成年
              Map<String, List<Person>> map2 = Stream.of(
                      new Person("张三", 18, 175)
                      , new Person("李四", 22, 177)
                      , new Person("张三", 14, 165)
                      , new Person("李四", 15, 166)
                      , new Person("张三", 19, 182)
              ).collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年"));
              map2.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v));
          }
      

      输出结果:

      k=李四    v=[Person{name='李四', age=22, height=177}, Person{name='李四', age=15, height=166}]
      k=张三    v=[Person{name='张三', age=18, height=175}, Person{name='张三', age=14, height=165}, Person{name='张三', age=19, height=182}]
      -----------
      k=未成年    v=[Person{name='张三', age=14, height=165}, Person{name='李四', age=15, height=166}]
      k=成年    v=[Person{name='张三', age=18, height=175}, Person{name='李四', age=22, height=177}, Person{name='张三', age=19, height=182}]

      多级分组: 先根据name分组然后根据年龄分组。

          public static void main(String[] args){
              // 先根据name分组,然后根据age(成年和未成年)分组
              Map<String,Map<Object,List<Person>>> map =  Stream.of(
                      new Person("张三", 18, 175)
                      , new Person("李四", 22, 177)
                      , new Person("张三", 14, 165)
                      , new Person("李四", 15, 166)
                      , new Person("张三", 19, 182)
              ).collect(Collectors.groupingBy(
                      Person::getName
                      ,Collectors.groupingBy(p->p.getAge()>=18?"成年":"未成年"
                      )
              ));
              map.forEach((k,v)->{
                  System.out.println(k);
                  v.forEach((k1,v1)->{
                      System.out.println("\t"+k1 + "=" + v1);
                  });
              });
          }
      

      输出结果:

      李四
          未成年=[Person{name='李四', age=15, height=166}]
          成年=[Person{name='李四', age=22, height=177}]
      张三
          未成年=[Person{name='张三', age=14, height=165}]
          成年=[Person{name='张三', age=18, height=175}, Person{name='张三', age=19, height=182}]

      对流中的数据做分区操作

      Collectors.partitioningBy会根据值是否为true,把集合中的数据分割为两个列表,一个true列表,一个false列表。

          public static void main(String[] args){
              Map<Boolean, List<Person>> map = Stream.of(
                      new Person("张三", 18, 175)
                      , new Person("李四", 22, 177)
                      , new Person("张三", 14, 165)
                      , new Person("李四", 15, 166)
                      , new Person("张三", 19, 182)
              ).collect(Collectors.partitioningBy(p -> p.getAge() > 18));
              map.forEach((k,v)-> System.out.println(k+"\t" + v));
          }
      

      输出结果:

      false    [Person{name='张三', age=18, height=175}, Person{name='张三', age=14, height=165}, Person{name='李四', age=15, height=166}]
      true    [Person{name='李四', age=22, height=177}, Person{name='张三', age=19, height=182}]

      对流中的数据做拼接

      Collectors.joining会根据指定的连接符,将所有的元素连接成一个字符串。

          public static void main(String[] args){
              String s1 = Stream.of(
                      new Person("张三", 18, 175)
                      , new Person("李四", 22, 177)
                      , new Person("张三", 14, 165)
                      , new Person("李四", 15, 166)
                      , new Person("张三", 19, 182)
              ).map(Person::getName)
                      .collect(Collectors.joining());
              // 张三李四张三李四张三
              System.out.println(s1);
              String s2 = Stream.of(
                      new Person("张三", 18, 175)
                      , new Person("李四", 22, 177)
                      , new Person("张三", 14, 165)
                      , new Person("李四", 15, 166)
                      , new Person("张三", 19, 182)
              ).map(Person::getName)
                      .collect(Collectors.joining("_"));
              // 张三_李四_张三_李四_张三
              System.out.println(s2);
              String s3 = Stream.of(
                      new Person("张三", 18, 175)
                      , new Person("李四", 22, 177)
                      , new Person("张三", 14, 165)
                      , new Person("李四", 15, 166)
                      , new Person("张三", 19, 182)
              ).map(Person::getName)
                      .collect(Collectors.joining("_", "###", "$$$"));
              // ###张三_李四_张三_李四_张三$$$
              System.out.println(s3);
          }
      

      并行的Stream流

      串行的Stream流

      我们前面使用的Stream流都是串行,也就是在一个线程上面执行。

      并行流

      parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度。

      获取并行流

      我们可以通过两种方式来获取并行流。

      • 通过List接口中的parallelStream方法来获取
      • 通过已有的串行流转换为并行流(parallel)
          public static void main(String[] args){
              List<Integer> list = new ArrayList<>();
              // 通过List 接口 直接获取并行流
              Stream<Integer> integerStream = list.parallelStream();
              // 将已有的串行流转换为并行流
              Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();
          }
      

      并行流操作

          public static void main(String[] args){
              Stream.of(1,4,2,6,1,5,9)
                      .parallel() // 将流转换为并发流,Stream处理的时候就会通过多线程处理
                      .filter(s->{
                          System.out.println(Thread.currentThread() + " s=" +s);
                          return s > 2;
                      }).count();
          }
      

      并行流和串行流对比

      我们通过for循环,串行Stream流,并行Stream流来对500000000亿个数字求和。来看消耗时间。

      public class Test {
          private static long times = 500000000;
          private  long start;
          @Before
          public void befor(){
              start = System.currentTimeMillis();
          }
          @After
          public void end(){
              long end = System.currentTimeMillis();
              System.out.println("消耗时间:" + (end - start));
          }
          /**
           * 普通for循环 消耗时间:138
           */
          @Test
          public void test01(){
              System.out.println("普通for循环:");
              long res = 0;
              for (int i = 0; i < times; i++) {
                  res += i;
              }
          }
          /**
           * 串行流处理
           *   消耗时间:203
           */
          @Test
          public void test02(){
              System.out.println("串行流:serialStream");
              LongStream.rangeClosed(0,times)
                      .reduce(0,Long::sum);
          }
          /**
           * 并行流处理 消耗时间:84
           */
          @Test
          public void test03(){
              LongStream.rangeClosed(0,times)
                      .parallel()
                      .reduce(0,Long::sum);
          }
      }
      

      通过案例我们可以看到parallelStream的效率是最高的。

      Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是一个线程操作。

      线程安全问题

      在多线程的处理下,肯定会出现数据安全问题。如下:

          @Test
          public void test(){
              List<Integer> list = new ArrayList<>();
              for (int i = 0; i < 1000; i++) {
                  list.add(i);
              }
              System.out.println(list.size());
              List<Integer> listNew = new ArrayList<>();
              // 使用并行流来向集合中添加数据
              list.parallelStream()
                      //.forEach(s->listNew.add(s));
                      .forEach(listNew::add);
              System.out.println(listNew.size());// 839
          }
      

      针对这个问题,我们的解决方案有哪些呢?

      • 加同步锁
      • 使用线程安全的容器
      • 通过Stream中的toArray/collect操作

      以上就是Java8新特性 StreamAPI实例详解的详细内容,更多关于Java8新特性 StreamAPI的资料请关注自由互联其它相关文章!

      上一篇:SpringCloud集成Sleuth和Zipkin的思路讲解
      下一篇:没有了
      网友评论