Java1.8新特性 -- Srream流

2020-06-10 15:56:21 浏览数 (1)

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,分组等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。其形式好比下面的图:

代码语言:javascript复制
 --------------------         ------     ------     ---     ------- 
| stream of elements  -----> |filter -> |sorted -> |map -> |collect|
 --------------------         ------     ------     ---     ------- 

以上的流程转换为 Java 代码为:

代码语言:javascript复制
List<Integer> newList = list.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();
此方式每次操作都返回一个新的流,可以再转为集合,而不改变原始集合的值。
Strean流的创建:
代码语言:javascript复制
// 1,校验通过Collection 系列集合提供的stream()或者paralleStream()
List<String> list = new ArrayList<>();
Strean<String> stream1 = list.stream();

// 2.通过Arrays的静态方法stream()获取数组流
String[] str = new String[10];
Stream<String> stream2 = Arrays.stream(str);

// 3.通过Stream类中的静态方法of
Stream<String> stream3 = Stream.of("aa","bb","cc");
//of方法还可以装多个集合
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5));

// 4.创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x 2);
方法介绍:
1.forEach():循环迭代流中的每个数据
代码语言:javascript复制
    //java 8 前
    System.out.println("java 8 前");
    for(User user: list){
      System.out.println(user);
    }
    // java 8 lambda
    System.out.println("java 8 lambda");
    list.forEach(user -> System.out.println(user));
 
    // java 8 stream lambda
    System.out.println("java 8 stream lambda");
    list.stream().forEach(user -> System.out.println(user));

2.collect(): 从流转成集合

collect方法十分强大,可以将流转成各种集合,甚至还能对进行分组等操作

代码语言:javascript复制
    List<User> list = Arrays.asList(
      new User("张三", 11),
      new User("王五", 20),
      new User("王五", 91)
    );
   
    //将流转换为list集合
    List<User> newList = list.stream().collect(Collectors.toList());
    System.out.println(newList.toString());

    //将流转换为map集合(以age为key,以name为value值)
    Map<Integer, String> map = list.stream().collect(Collectors.toMap(User::getAge, User::getName));
  //[User{age=11, name='张三'}, User{age=20, name='王五'}, User{age=91, name='王五'}]
    System.out.println(map.toString());//{20=王五, 91=王五, 11=张三}

  //以name相同进行分组
    Map<String, List<User>> groupMap = list.stream()
      .collect(Collectors.groupingBy(User::getName));
    System.out.println(groupMap);
  //{张三=[User{age=11, name='张三'}], 王五=[User{age=20, name='王五'}, User{age=91, name='王五'}]}
这里需要注意的问题是,上面每次调用方法都用list.stream(),如果你想把它提取出来,想下面这样运行就会报错,因为stream流每次使用完以后就自动关闭了。
代码语言:javascript复制
 //获取stewam流
 Stream<User> stream = list.stream();
 //将流转换为list集合
 List<User> newList = stream.collect(Collectors.toList());
  
 //继续往下面走就会报错,因为此时stream流已经关闭了!!!
 Map<Integer, String> map = stream.collect(Collectors.toMap(User::getAge, User::getName));

错误信息如下:

代码语言:javascript复制
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.wenxue.jdk8.StreamTest.main(StreamTest.java:147)

3.filter(): 过滤,可以对原集合进行过滤,从而输出满足条件的新集合。

代码语言:javascript复制
  List<Integer> numList = Arrays.asList(1,2,3,4,5);
  List<Integer> collect3 = numList.stream().filter(n -> n>3).collect(Collectors.toList());
  System.out.println(collect3); //[4, 5]

如上所示,filter(n -> n>3)的第一个n是遍历的集合值,第二个表达式n>3表示要输出集合中大于3的数,于是就成功的过滤了原集合,踢掉了小于表达式的值,得到了新的集合[4,5]。

4.map(): 增强集合,可以对集合中的值进行操作,得到新的值,从而得到新的集合。

代码语言:javascript复制
   List<String> list = Arrays.asList("a", "b", "c", "d");
  List<String> newList = list.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());
  System.out.println(newList); //[A, B, C, D]

如上所示,调用map方法,将原list的值都按表示式增强,进而转成大写,于是得到了增强后的集合。

map()与filter()的区别:

map()可以对集合原始进行操作,自定义返回新的结果集;而fliter是过滤(取满足条件的值),不能修改集合元素的值

5.sorted():排序

代码语言:javascript复制
 public void testSort() {
   
   List<Integer> nums = Arrays.asList(3, 1, 2, 5);

    // java 1.8 前
    System.out.println("java 8 前");
    Collections.sort(list, new Comparator<User>() {
      @Override
      public int compare(User o1, User o2) {
        return o1.getAge().compareTo(o2.getAge());
      }
    });
   
    // java 1.8 stream 方法引用
   nums.stream().sorted().forEach(a -> System.out.println(a));//1 2 3 5
  }

从上面我们可以看出,在jdk1.8以前要实现一个排序十分的麻烦,而使用jdk1.8却可以很简单的就做到了。

如果你还想指定一个排序规则,1.8也能满足你,sorted()方法还可以提供一个参数,来用于指定比较规则:

代码语言:javascript复制
//使用User对象的年龄排序
list.stream().sorted(Comparator.comparing(User::getAge))
       .forEach(user -> System.out.println(user));

6.limit():截断,将集合截断,取截断前的值

代码语言:javascript复制
 @Test
  public void testLimit(List<User> list) {
    // 从第三个开始截断,只输出前三个
    // java 8 前
    System.out.println("java 8 前");
    for (int i = 0; i < 3; i  ) {
      System.out.println(list.get(i));
    }
    // java 8 stream
    System.out.println("java 8 stream");
    list.stream().limit(3).forEach(user -> System.out.println(user));
  }
7.skip():截断,将集合截断,取截断后的值(与limi相反)
代码语言:javascript复制
 @Test
  public void testSkip() {
    // 跳过前三个元素,从第四个开始输出
    // java 8 前
    System.out.println("java 8 前");
    for (int i = 3; i < list.size(); i  ) {
      System.out.println(list.get(i));
    }
    // java 8 stream
    System.out.println("java 8 stream");
    list.stream().skip(3).forEach(user -> System.out.println(user));
  }
8.distinct():去重,注意:必须重写对应泛型的hashCode()和equals()方法
代码语言:javascript复制
   /**
   * 使用jdk1.8的distinct() 去重,注意:必须重写对应泛型的hashCode()和equals()方法
   */
  @Test
  public void testDistinct() {
    // 注意使用Arrays.asList() 返回的是Arrays的内部类ArrayList,操作remove,add会报错
    List<User> users = new ArrayList(list);
    // 为list去除重复数据
    // java 8 前
    System.out.println("java 8 前");
    for (int i = 0; i < users.size() - 1; i  ) {
      for (int j = users.size() - 1; j > i; j--) {
        if (users.get(j).getAge() == users.get(i).getAge() && users.get(j).getName()
            .equals(users.get(i).getName())) {
          users.remove(i);
        }
      }
    }
    for (User user : users) {
      System.out.println(user);
    }
    // java 8 stream
    System.out.println("java 8 stream");
    users.stream().distinct().forEach(user -> System.out.println(user));
  }
实战例子: 按照年龄大于18去重,从小到大排序,并且只取前二个
代码语言:javascript复制
  list.stream().filter(user1 -> user1.getAge() > 18).distinct().sorted(
                Comparator.comparing(User::getAge)).limit(2)
            .forEach(user1 -> System.out.println(user));

9.mapToInt():计算,使用该方法可进行max,min,sum,avg,count等计算

代码语言:javascript复制
  /**
   * 测试计算,mapToInt()方法是先返回一个可计算的对象,进而才能调用计算方法
   */
  @Test
  public void testNum() {
    IntSummaryStatistics num = list.stream().mapToInt(u -> u.getAge())
        .summaryStatistics();
    System.out.println("总共人数:"   num.getCount());
    System.out.println("平均年龄:"   num.getAverage());
    System.out.println("最大年龄:"   num.getMax());
    System.out.println("最小年龄:"   num.getMin());
    System.out.println("年龄之和:"   num.getSum());
  }
10.findFirst() :使用该方法获取第一个元素
代码语言:javascript复制
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    Intger num = list.stream().findFirst().get();
    System.out.println(num);//1
11.flatMap():合并流,将原来的stream中的所有元素都展开组成一个新的stream。

上面我们讲到了使用of()方法可以创建集合,并且可以包裹多个集合,那么又怎样将多个再合并呢?使用flatMap()方法就能做到。

代码语言:javascript复制
  @Test
  public void testFlatMap() {
    //创建一个 装有两个泛型为integer的集合
    Stream<List<Integer>> stream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4, 5));
    // 将两个合为一个
    Stream<Integer> newStream = stream.flatMap(
        (Function<List<Integer>, Stream<Integer>>) integers -> integers.stream());
    // 转为新的集合
    List<Integer> collect = newStream.collect(toList());
    System.out.println("新stream大小:" collect.size());//5
    System.out.println("-----合并后-----");
    collect.forEach(o -> System.out.println(o));//1,2,3,4,5
  }
12.reduce() :智能运算

reduce 操作可以实现从一组元素中生成一个值

sum()max()min()count()等都是reduce操作,因为常用所以设为单独函数

例子1:找到年龄最大的,并求所有人的年龄之和
代码语言:javascript复制
  @Test
  public void reduce() {
    List<User> list = Arrays.asList(
        // name,age
        new User("张三", 11),
        new User("王五", 20),
        new User("王五", 91)
    );
 
    Optional<User> reduce = list.stream().reduce((s1, s2) -> s1.getAge() > s2.getAge() ? s1 : s2);
    User user = reduce.get();
    System.out.println(user); //User{age=91, name='王五'}
    
    //当然也可以使用更简单的方法max()
    Optional<User> max = list.stream().max(Comparator.comparing(User::getAge));
        User user2 = max.get();
        System.out.println(user2);// //User{age=91, name='王五'}
    
    // 求年龄之和
    Integer reduce3 = list.stream().reduce(0, // 该参数为初始值
                (integer, user3) -> integer   user3.getAge(), // 该参数为累加器
                (integer, integer2) -> integer   integer2);// 多个部分累加
    System.out.println(reduce); //122
  }

这里我要说明的是reduce类似于智能运算,它是一种设计思想,其中的max,count等之类的方法已经实现了这种模式。

13.使用collect()做字符串join
代码语言:javascript复制
  @Test
  public void reduce() {
    // 使用Collectors.joining()拼接字符串
    Stream<String> stream = Stream.of("张三","李四","王五","赵六");
//    String s = stream.collect(Collectors.joining()); // 张三李四王五赵六
//    String s = stream.collect(Collectors.joining("-")); // 张三-李四-王五-赵六
    String s = stream.collect(Collectors.joining("-", "(", ")")); // (张三-李四-王五-赵六)
    System.out.println(s);
  }

以上就是java1.8的Stream流的所有操作方法,看以看出1.8的版本还是蛮强大的,同学们赶紧学习下吧! -- 龚文学

0 人点赞