Java8新特性 Stream API

Stream API

一、简介

1. 概念

  • 使用 Stream API 对集合、数组数据进行操作,比如查找、过滤、映射数据等操作
  • 特点
    • Stream 不会存储元素
    • Stream 不会改变源数据,他们会返回一个持有结果的新Stream
    • Stream 操作是延迟执行的,这意味着他们会等到需要结果的时候才执行

2. 执行流程

  • 通过数据源获取Stream
  • 一系列的中间操作(过滤、查找等构成的中间操作链
  • 终止操作

3. 注意事项

  • 执行终止操作时,才会执行中间操作
  • 如果没有执行终止操作,则中间操作链都不执行
  • 终止操作执行后,此Stream不可再用,如要使用,必须重新创建一个Stream实例

二、获取Stream

测试数据如下:

public class EmployeeData {
	public static List<Employee> getEmployees(){
		//创建一个List集合
		List<Employee> list = new ArrayList<>();
		
		list.add(new Employee(1001, "马化腾", 34, 6000.38));
		list.add(new Employee(1002, "马云", 12, 9876.12));
		list.add(new Employee(1003, "刘强东", 33, 3000.82));
		list.add(new Employee(1004, "雷军", 26, 7657.37));
		list.add(new Employee(1005, "李彦宏", 65, 5555.32));
		list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
		list.add(new Employee(1007, "任正非", 26, 4333.32));
		list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
		
		return list;
	}
}

有以下四种方式获取Stream

1. 通过集合获取

  • 顺序流
    • 操作集合的数据时按照集合中数据的顺序进行操作
    • 调用Collection接口的default Stream<E> stream()获取
  • 并行流
    • 操作集合的数据时多个线程并行的操作数据,不一定是原始集合的顺序
    • 调用Collection接口的default Stream<E> parallelStream()获取
@Test
public void test1(){
    List<Employee> employees = EmployeeData.getEmployees();

    //获取顺序流
    Stream<Employee> stream = employees.stream();

    //获取并行流
    Stream<Employee> parallelStream = employees.parallelStream();
}

2. 通过数组获取

  • 调用Arrays类的static <T> Stream<T> stream(T[] array)获取
@Test
public void test2(){
    //1. int型数组
    int[] arr = new int[]{1,2,3,4,5,6};
    IntStream stream = Arrays.stream(arr);

    //2. 自定义数组
    Employee e1 = new Employee(1001,"Tom");
    Employee e2 = new Employee(1002,"Jerry");
    Employee[] arr1 = new Employee[]{e1,e2};
    Stream<Employee> stream1 = Arrays.stream(arr1);
}

3. 通过Stream.of()获取

  • 调用Stream接口的public static<T> Stream<T> of(T... values)获取
  • 此方法可以接收任意数量的参数
@Test
public void test3(){
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}

4. 无限流的获取

  • 上述操作的数据源都是有限的,还可以创建无限个元素的流,有下述两种方式

  • 迭代方式:调用Stream接口的public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)获取

    • seed参数表示数据源的起始数据
    • UnaryOperator参数是内置函数式接口Function的子接口,其中的抽象方法指定如何迭代数据
  • 生成方式:调用Stream接口的public static<T> Stream<T> generate(Supplier<T> s)获取

    • Supplier是内置的供给型接口
@Test
public void test4(){
    //迭代方式
    //遍历前10个偶数
    
    //forEach是终止操作,参数是消费型接口的实例,作用是遍历流中的数据并执行相关的操作
   
    //PrintStream ps = System.out;
    //PrintStream中的println方法定义:public void println(Object x),满足方法引用格式
    
    Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
    //运行结果:输出了10个数据


    //生成方式
    //生成10个随机的Double型数据
    
    //Math类中的random方法定义:public static double random(),满足方法引用格式
    
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
    //运行结果:输出了10个数据
}

三、Stream的中间操作

1. 筛选和切片

@Test
public void test1(){
    List<Employee> list = EmployeeData.getEmployees();

    //1. filter(Predicate p) —— 从流中排除某些元素

    Stream<Employee> stream = list.stream();
    //练习:查询员工表中薪资大于7000的员工信息
    //test方法的参数类型是泛型的类型
    stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);

    //2. limit(n) —— 使流中元素不超过给定数量

    //必须重新生成一个Stream
    list.stream().limit(3).forEach(System.out::println);

    //3. skip(n) —— 跳过前n个元素,返回一个扔掉了前n个元素的流;若流中元素不足n个,则返回一个空流

    list.stream().skip(3).forEach(System.out::println);

    //4. distinct() —— 通过流中元素的hashCode()和equals(),去除重复元素

    list.stream().distinct().forEach(System.out::println);
}

2. 映射

@Test
public void test2(){

    //1. map(Function f) —— 根据函数给定的要求,将元素转换成其他形式或提取信息,然后将这些元素映射成一个流

    //练习1:将集合中的字母转换成大写
    List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
    list.stream().map(String::toUpperCase).forEach(System.out::println);

    //练习2:获取员工姓名长度大于3的员工的姓名。
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<String> namesStream = employees.stream().map(Employee::getName);
    namesStream.filter(name -> name.length() > 3).forEach(System.out::println);

    //2. flatMap(Function f) —— 根据函数给定的要求,将流中的每个值都换成另一个流,然后把所有流连接成一个流

    //flatMap类似于Collection的addAll方法
    //map类似于Collection的add方法
}

3. 排序

@Test
public void test3(){

    //1. sorted() —— 自然排序

    List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
    list.stream().sorted().forEach(System.out::println);
    //要求泛型的类必须实现Comparable接口

    //2. sorted(Comparator com)——定制排序

    List<Employee> employees = EmployeeData.getEmployees();
    employees.stream().sorted( (e1,e2) -> {
        //员工按照年龄排序,年龄相同的按照工资降序
        int ageValue = Integer.compare(e1.getAge(),e2.getAge());
        if(ageValue != 0){
            return ageValue;
        }else{
            return -Double.compare(e1.getSalary(),e2.getSalary());
        }

    }).forEach(System.out::println);
}

四、Stream的终止操作

  • 有终止操作时,才会执行中间操作

1. 匹配与查找

@Test
public void test1(){
    List<Employee> employees = EmployeeData.getEmployees();

    //1. allMatch(Predicate p) —— 检查根据参数的规则是否匹配所有元素

    //是否所有的员工的年龄都大于18
    boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);

    //2. anyMatch(Predicate p) —— 检查根据参数的规则是否至少匹配一个元素

    //是否存在员工的工资大于10000
    boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);

    //3. noneMatch(Predicate p) —— 检查根据参数的规则是否没有匹配的元素

    //是否存在员工姓“雷”
    boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));

    //4. findFirst —— 返回第一个元素

    Optional<Employee> employee = employees.stream().findFirst();

    //5. findAny —— 返回当前流中的任意元素

    Optional<Employee> employee1 = employees.parallelStream().findAny();

    //6. count —— 返回流中元素的总个数

    long count = employees.stream().filter(e -> e.getSalary() > 5000).count();

    //7. max(Comparator c) —— 返回流中最大值

    //返回最高的工资
    Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
    Optional<Double> maxSalary = salaryStream.max(Double::compare);

    //8. min(Comparator c) —— 返回流中最小值

    //9. forEach(Consumer c) —— 内部迭代

    employees.stream().forEach(System.out::println);
}

2. 归约

@Test
public void test(){

    //1. reduce(T identity, BinaryOperator<T> accumulator) —— 将流中元素反复结合起来,得到一个值

    /**
    * 参数1 表示计算的起始值
    * 参数2 BinaryOperator extends BiFunction<T,T,T>
    *      BiFunction属于Function函数式接口,其中的抽象方法接收两个值,返回一个值
    */

    //计算1-10的自然数的和
    List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    //Integer中有方法public static int sum(int a, int b),符合方法引用规则
    Integer sum = list.stream().reduce(0, Integer::sum);

    //2. reduce(BinaryOperator<T> accumulator) —— 将流中元素反复结合起来,得到一个值

    //计算公司所有员工工资的总和
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
    Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
}

3. 收集

@Test
public void test(){

    //collect(Collector c) —— 将流转换为其他集合的形式,接收者是Collector接口的实现

    //可以通过调用Collectors中的静态方法得到Collector的实例,并决定将流中的数据收集到哪里

    //查找工资大于6000的员工,结果返回为一个List
    List<Employee> employees = EmployeeData.getEmployees();
    List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());

}

Collectors类中的常用静态方法:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nice2cu_Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值