Java8新特性Stream的使用总结

一、Stream简介

Stream是Java8中处理集合的关键抽象概念,Stream API提供了一种高效且易于使用的处理数据的方式。你可以使用它对集合数据进行指定操作,如可以执行非常复杂的查找、过滤和映射数据等操作。

注意:

  1. Stream自己不会存储元素
  2. Stream不会改变源对象,Stream操作会返回一个新的Stream
  3. Stream操作是延迟执行的,这意味着等到获取结果时,Stream才会执行

Stream操作的三个步骤:

  1. Stream创建,由一个数据源,如集合、数组来获取一个Stream
  2. 中间操作。一个中间操作链,对数据源的数据进行处理
  3. 终止操作。执行中间操作链并产生结果。

Stream的操作步骤示例图:
在这里插入图片描述

二、Stream的创建

1.使用集合来创建Stream
Java8中的Collection接口被扩展,提供了两个获取Stream的方法:

//返回一个顺序流
default Stream<R> stream() 
//返回一个并行流
default Stream<E> parallelStream()

2.使用数组创建Stream
Java8中的Arrays的静态方法stream()可以获取流

static <T> Stream<T> stream(T[] array)
static IntStream<T> stream(int[] array)
static LongStream<T> stream(long[] array)
static DoubleStream<T> stream(double[] array)

3.由值创建Stream
使用Stream的静态方法of来创建一个流,该方法可以接收任意数量的参数。

static<T> Stream<T> of(T... values)

4.由函数创建Stream
可以使用Stream的静态方法iterate()和generate()创建无限流

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
public static<T> Stream<T> generate(Supplier<T> s)

Stream创建代码示例:

@Test
  public void test01(){
    //1.通过Collection系列集合提供的stream()或parallelStream()获取
    ArrayList<String> list = new ArrayList<>();
    Stream<String> stream1 = list.stream();
    //2.通过Arrays中的静态方法stream()获取
    String[] strings=new String[10];
    Stream<String> stream2 = Arrays.stream(strings);
    //3.通过Stream类中的静态方法of()获取
    Stream<String> stream3 = Stream.of(strings);
    //4.创建无限流
    //4.1迭代
    Stream<Integer> stream4 = Stream.iterate(0, x-> x + 2);
    //4.2生成
    Stream<Double> stream5 = Stream.generate(() -> Math.random());
  }

三、Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而是在种植操作时一次性全部处理,称为“惰性求值”。

1.筛选与切片

筛选与切片的相关方法介绍:
在这里插入图片描述
使用示例
创建一个Employee类用于后续使用:

public class Employee {
  private Integer id;
  private String name;
  private Integer age;
  private State state;

  public Employee(Integer id, String name, Integer age, State state) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.state = state;
  }

  @Override
  public Employee(){

  }

  public Employee(Integer id) {
    this.id = id;
  }

  public Employee(Integer id, Integer age) {
    this.id = id;
    this.age = age;
  }

  public Employee(int id, String name, int age) {
    this.id = id;
    this.name = name;
    this.age = age;
  }



  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Employee employee = (Employee) o;
    return Objects.equals(id, employee.id) &&
            Objects.equals(name, employee.name) &&
            Objects.equals(age, employee.age) &&
            state == employee.state;
  }

  @Override
  public int hashCode() {
    return Objects.hash(id, name, age, state);
  }
//省略getter、setter、toString方法
}

State枚举类的定义

public enum State {
  BUSY,
  FREE,
  VACATION
}

filter方法——接收Lambda,从流中排除某些元素

public class TestStreamAPI02 {
  List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
          new Employee(2,"李四",50),
          new Employee(3,"王五",45),
          new Employee(26,"赵六",45),
          new Employee(26,"赵六",45),
          new Employee(26,"赵六",45));
  //filter——接收Lambda,从流中排除某些元素
  @Test
  public void test01(){
    //中间操作:不会执行任何操作
    Stream<Employee> stream = employees.stream().filter((employee -> {
      System.out.println("执行过滤操作");
      return employee.getAge() > 45;
    }));
    //终止操作:一次性执行全部内容,即“惰性求值”
    stream.forEach(System.out::println);
  }
}

控制台输出结果:
在这里插入图片描述
limit——截断流,使其元素不超过给定数量

  @Test
  public void test02(){
    employees.stream()
            .filter(e->e.getAge()>18)
            .limit(3)
            .forEach(System.out::println);
  }

控制台输出结果:
在这里插入图片描述
skip–跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回空流。与limit互补。

  @Test
  public void test03(){
    employees.stream()
            .skip(2)
            .forEach(System.out::println);
  }

控制台输出结果:
在这里插入图片描述
distinct–筛选,去取流中的hashcode()和equals相同的元素

  @Test
  public void test04(){
    employees.stream()
            .distinct()
            .forEach(System.out::println);
  }

Employee类中重写了hashcode()和equals()方法根据id, name, age, state属性进行比较计算对象是否相等,以此控制台输出结果:
在这里插入图片描述

2.映射

映射相关方法如下:
在这里插入图片描述
使用示例
map方法——接收Lambda,将元素转化成其他形式或提取信息。接收一个函数作为参数, 该函数会被应用到每个元素上,并将其映射成一个新的元素。

public class TestStreamAPI03 {
  List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
          new Employee(2,"李四",50),
          new Employee(3,"王五",45),
          new Employee(26,"赵六",45),
          new Employee(26,"赵六",45),
          new Employee(26,"赵六",45));
  @Test
  public void test01(){
    List<String> list = Arrays.asList("a", "bb", "ccc", "dddd");
    //将list中元素转化成其他形式
    list.stream()
            .map(s->s.toUpperCase())
            .forEach(System.out::print);
    //原集合不受到影响
    list.forEach(System.out::print);
    //提取list中元素的信息
    employees.stream()
            .map(employee -> employee.getName())
            .forEach(System.out::print);
  }
  }

控制台输出结果:
在这里插入图片描述

flatMap方法–接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

public class TestStreamAPI03 {
  List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
          new Employee(2,"李四",50),
          new Employee(3,"王五",45),
          new Employee(26,"赵六",45),
          new Employee(26,"赵六",45),
          new Employee(26,"赵六",45));

  //提取字符串中的每个字符到list中,并放入stream流
  public static Stream<Character> filterCharacter(String str){
    ArrayList<Character> list = new ArrayList<>();
    for (Character ch:str.toCharArray()) {
      list.add(ch);
    }
    return list.stream();
  }
  //测试flatMap
  @Test
  public void test02(){
    List<String> list = Arrays.asList("a", "bb", "ccc", "dddd");
    Stream<Stream<Character>> streams = list.stream()
            .map(TestStreamAPI03::filterCharacter);//得到的流的形式类似{{a},{b,b},{c,c,c},{d,d,d,d}}
    //不使用flatMap函数
    System.out.println("不使用flatMap函数");
    streams.forEach(stream->stream.forEach(System.out::print));
    //使用flatMap函数
    System.out.println("\n使用flatMap函数");
    Stream<Character> streams2 = list.stream()
            .flatMap(TestStreamAPI03::filterCharacter);//得到流的形式类似{a,b,b,c,c,c,d,d,d,d}
    streams2.forEach(System.out::print);
  }
}

控制台输出结果:
在这里插入图片描述

3.排序

排序的相关方法:
在这里插入图片描述
使用示例:
sorted()–产生一个新流,自然排序(Comparable)

public class TestStreamAPI04 {
  List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
          new Employee(2,"李四",50),
          new Employee(3,"王五",45),
          new Employee(23,"赵六",45),
          new Employee(21,"田七",45),
          new Employee(24,"天启",45));
  @Test
  public void test01(){
    List<String> list = Arrays.asList("ddd", "ccc", "bbb", "aaa");
    //默认使用泛型的实现Comparable接口中的compareTo方法进行比较
    list.stream().sorted().forEach(System.out::println);
  }
}

sorted(Comparator com)–产生一个新流,定制排序(Comparator)

  @Test
  public void test02(){
    employees.stream().sorted((e1,e2)->{
      if (e1.getAge().equals(e2.getAge())){
        return e1.getId().compareTo(e2.getId());
      }else{
        return e1.getAge().compareTo(e2.getAge());
      }
    }).forEach(System.out::println);
  }

四、Stream的终止操作

终端操作会从stream的流水线生成结果,其结果可以不是流的值,可以是List,Integer,甚至是Void。

1.查找与匹配

查找与匹配的相关方法:
在这里插入图片描述
在这里插入图片描述
测试 allMatch,anyMatch,noneMatch方法的使用

public class TestStreamAPI05 {
  List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
          new Employee(2,"李四",50, State.BUSY),
          new Employee(3,"王五",45,State.BUSY),
          new Employee(23,"赵六",45,State.FREE),
          new Employee(21,"田七",40,State.VACATION),
          new Employee(24,"天启",35,State.FREE));
  //测试allMatch,anyMatch,noneMatch
  @Test
  public void test01(){
    boolean allMatch = employees.stream().allMatch(e -> e.getAge() >= 45);
    System.out.println(allMatch);
    boolean anyMatch = employees.stream().anyMatch((e -> e.getAge() > 65));
    System.out.println(anyMatch);
    boolean noneMatch = employees.stream().noneMatch((e -> e.getAge() > 65));
    System.out.println(noneMatch);
  }
}

控制台输出结果:
在这里插入图片描述

测试findFirst,findAny方法的使用

 //测试findFirst,findAny
  @Test
  public void test02(){
    Optional<Employee> first = employees.stream()
            .sorted((e1, e2) -> e1.getAge().compareTo(e2.getAge()))
            .findFirst();
    System.out.println(first.get());
    Optional<Employee> any = employees.stream().filter(e -> e.getState()==State.FREE).findAny();
    System.out.println(any.get());
  }

控制台输出结果:
在这里插入图片描述
测试count,max,min方法的使用

 //测试count,max,min
  @Test
  public void test03(){
    //测试count
    long count = employees.stream().count();
    System.out.println(count);
    //测试max
    Optional<Employee> max = employees.stream().max((e1, e2) -> e1.getAge().compareTo(e2.getAge()));
    System.out.println(max.get());
    //测试min
    Optional<Employee> min = employees.stream().min((e1, e2) -> e1.getAge().compareTo(e2.getAge()));
    System.out.println(min.get());
  }

控制台输出的结果:
在这里插入图片描述

2.规约

规约相关方法:
在这里插入图片描述
测试reduce(T identity,BinaryOperator)和reduce(BinaryOperator),其中identity为原始的结合值。

public class TestStreamAPI06 {
  List<Employee> employees=Arrays.asList(new Employee(1,"张三",60),
          new Employee(2,"李四",50, State.BUSY),
          new Employee(3,"王五",45,State.BUSY),
          new Employee(23,"赵六",45,State.FREE),
          new Employee(21,"田七",40,State.VACATION),
          new Employee(24,"天启",35,State.FREE));
  @Test
  public void test01(){
    //计算整数数组累加
    List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    Integer result = integers.stream().reduce(5, (x, y) -> x + y);
    System.out.println(result);
    //计算平均年龄
    Optional<Integer> ageTotal = employees.stream().map(Employee::getAge).reduce(Integer::sum);
    long empNum=employees.stream().count();
    System.out.println(ageTotal.get()/empNum);
  }
}

控制台输出结果:
在这里插入图片描述

3.收集

收集的方法定义
在这里插入图片描述
Collector接口中的方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。Collectors实用类提供了很多静态方法,可以方便地创建常用的收集器实例,具体方法与实例如下图:
在这里插入图片描述
在这里插入图片描述
测试将流转换为其他形式,收集到新的集合上,包括Collectors.toList()、Collectors.toSet()、Collectors.toCollection(集合对象)

  List<Employee> employees=Arrays.asList(new Employee(1,"张三",60,State.VACATION),
          new Employee(2,"李四",50, State.BUSY),
          new Employee(3,"王五",45,State.BUSY),
          new Employee(23,"赵六",45,State.FREE),
          new Employee(23,"赵六",45,State.FREE),
          new Employee(21,"田七",40,State.VACATION),
          new Employee(24,"天启",35,State.FREE));
  @Test
  public void test01(){
    List<String> list = employees.stream().map(Employee::getName).collect(Collectors.toList());
    list.forEach(System.out::println);
    System.out.println("-----------------");
    Set<String> set = employees.stream().map(Employee::getName).collect(Collectors.toSet());
    set.forEach(System.out::println);
    System.out.println("-----------------");
    HashSet<String> hashSet = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
    hashSet.forEach(System.out::println);
  }
  }

数组统计相关方法,包括counting、averagingDouble、summingInt等方法。

  @Test
  public void test02(){
    //总数
    Long sum= employees.stream().collect(Collectors.counting());
    System.out.println(sum);
    //平均值
    Double averageAge = employees.stream().collect(Collectors.averagingDouble(Employee::getAge));
    System.out.println(averageAge);
    //某属性的综合
    Integer ageSum = employees.stream().collect(Collectors.summingInt(Employee::getAge));
    System.out.println(ageSum);
    //最大值
    Optional<Employee> maxAgeEmployee = employees.stream().collect(Collectors.maxBy((e1, e2) -> e1.getAge().compareTo(e2.getAge())));
    System.out.println(maxAgeEmployee.get());
    //最小值
    Optional<Employee> minAgeEmployee = employees.stream().collect(Collectors.minBy((e1, e2) -> e1.getAge().compareTo(e2.getAge())));
    System.out.println(minAgeEmployee.get());
    //所有数据
    IntSummaryStatistics totalData = employees.stream().collect(Collectors.summarizingInt(Employee::getAge));
    System.out.println(totalData);
  }

控制台输出结果:
在这里插入图片描述
测试分组groupingBy方法

  @Test
  public void test03(){
    Map<State, List<Employee>> stateListMap = employees.stream().collect(Collectors.groupingBy(Employee::getState));
    System.out.println(stateListMap);
  }

程序输出结果:

{FREE=[Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=24, name='天启', age=35, state=FREE}], VACATION=[Employee{id=1, name='张三', age=60, state=VACATION}, Employee{id=21, name='田七', age=40, state=VACATION}], BUSY=[Employee{id=2, name='李四', age=50, state=BUSY}, Employee{id=3, name='王五', age=45, state=BUSY}]}

测试多级分组:

  @Test
  public void test04(){
    Map<State, Map<String, List<Employee>>> collect = employees.stream()
            .collect(Collectors.groupingBy(Employee::getState, Collectors.groupingBy((e) -> {
              if (((Employee) e).getAge() <= 30) {
                return "青年";
              } else if (((Employee) e).getAge() <= 50) {
                return "中年";
              } else {
                return "老年";
              }
            })));
    System.out.println(collect);
  }

控制台输出结果:

{VACATION={老年=[Employee{id=1, name='张三', age=60, state=VACATION}], 中年=[Employee{id=21, name='田七', age=40, state=VACATION}]}, FREE={中年=[Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=24, name='天启', age=35, state=FREE}]}, BUSY={中年=[Employee{id=2, name='李四', age=50, state=BUSY}, Employee{id=3, name='王五', age=45, state=BUSY}]}}

测试分区方法partitioningBy,接收Predicate,满足属于true,不满足就属于false。

  //分区
  @Test
  public void test08(){
    Map<Boolean, List<Employee>> collect = employees.stream().collect(Collectors.partitioningBy(employee -> employee.getAge() > 40));
    System.out.println(collect);

  }

程序输出结果:

{false=[Employee{id=21, name='田七', age=40, state=VACATION}, Employee{id=24, name='天启', age=35, state=FREE}], true=[Employee{id=1, name='张三', age=60, state=VACATION}, Employee{id=2, name='李四', age=50, state=BUSY}, Employee{id=3, name='王五', age=45, state=BUSY}, Employee{id=23, name='赵六', age=45, state=FREE}, Employee{id=23, name='赵六', age=45, state=FREE}]}

笔记总结自:https://www.bilibili.com/video/BV14W411u7Ly?from=search&seid=6050719224094931659

  • 17
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值