stream流(三)

在这里插入图片描述

Stream流的操作

1.什么是Stream

它是一个数据渠道,用于操作数据源(集合,数组等)所生成的元素序列,集合讲的是数据,Stream讲的是计算!使用Stream API对集合数据进行操作,就类似使用SQL执行的数据库查询!

2.为什么要使用Stream API

实际开发中,项目中多数数据源来自Mysql,Oracel关系型数据库,但现在
还有来自Nosql的数据:如MongoDB,Redis,而这些NoSQL的数据就需要
java层面去处理

3.Stream的操作三个步骤

1.创建Stream:一个数据源(集合,数组),获取Stream
2.中间操作:一个中间操作链,对数据源进行一系列处理
3.终止操作:一旦执行终止操作,就执行中间操作,并产生结果,之后不会再

一:Stream流的实例化

四种方式:
a:通过集合对象.stream()

b:通过Arrays类的stream()

c:stream本身的of()

d:stream本身的iterate()、generate()

package cn.itcast.stream;

import cn.itcast.lambda.Employee;
import cn.itcast.lambda.EmployeeData;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 1.
 *  Stream关注的是数据的运算,和cpu打交道
 *  集合关注的是数据的存储,和内存打交道!
 *
 * 2.
 *  a.Stream自己不会存储元素
 *  b.Stream不会改变源对象,相反他们会返回一个持有结果的新Stream
 *  c.Stream操作是延迟执行的,这意味着他们会等需要结果的时候才去执行(懒加载)
 *
 * 3.Stream执行流程
 *  a.Stream的实例化
 *  b.一系列的中间过程(过滤,映射...)
 *  c.终止操作!
 *
 * 4.说明
 *  a.一个中间操作链,对数据源的数据进行处理
 *  b.一旦执行终止操作,就执行中间操作链,并产生结果,之后不会再被使用!
 */
public class StreamTest {

    //创建 Stream方式一: 通过集合
    @Test
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();
        // default Stream<E> stream():返回一个顺序流
        Stream<Employee> stream =  employees.stream();
        //default Stream<E> parallelStream(): 返回一个并行流
        Stream<Employee> employeeStream = employees.parallelStream();
    }

    //创建 Stream方式二: 通过数组
    @Test
    public void test2(){
        int[] arr = {1,3,2,4};
        //调用Arrays类的public static <T> Stream<T> stream(T[] array):返回一个流!
        IntStream intStream = Arrays.stream(arr);

        Employee employee1 = new Employee(1001,"wzj",22,50000);
        Employee employee2 = new Employee(1002,"tom",22,50000);
        Employee[] employees = new Employee[]{employee1,employee2};
        Stream<Employee> stream = Arrays.stream(employees);
    }

    //创建 Stream方式三: 通过stream本身的of()
    @Test
    public void test3(){
        Stream<String> stringStream = Stream.of("1", "2", "3");
    }

    //创建 Stream方式四: 创建无限流
    /**
     * UnaryOperator也是一个函数式接口!,所以可以用lambda表达式的方式!
     */
    @Test
    public void test4(){
        //迭代
        //public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        //遍历前10给个偶数
        Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);

        //生成
        //public static<T> Stream<T> generate(Supplier<T> s)
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }
}

二:Stream流的中间操作

三大分类
a:筛选与切片:filter()、limit()、skip()、distinct()

b:映射:map()、flatMap()

c:排序:sorted()、sorted(Comparator c)

  • 准备数据!!!
@Data
public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

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

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

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

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

    public static List<Employee> getEmployees(){
        List<Employee> list = new ArrayList();
        list.add(new Employee(1001,"wzj",22,50000));
        list.add(new Employee(1002,"tom",22,40000));
        list.add(new Employee(1003,"zhangsan",24,30000));
        list.add(new Employee(1004,"lisi",25,20000));
        list.add(new Employee(1005,"wangwu",26,10000));
        list.add(new Employee(1005,"wangwu",26,10000));
        return list;
    }
}

2.1:筛选与切片

public class StreamAPITest1 {

    //1.筛选与切片
    @Test
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();
        //1.filter(Predicate p) 接收lambda表达式,从流中排除某些元素
        //查询员工表中工资>10000的所有员工!
        employees.stream().filter(e -> e.getSalary()>10000).forEach(System.out::println); //注意stream流关闭之后,必须重新开一个去使用!
        System.out.println();

        //2.limit(n) 截断流,使其元素不超过给定数量
        //查询员工表中前三条记录!
        employees.stream().limit(3).forEach(System.out::println);
        System.out.println();

        //3.skip(n) 跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回一个空流
        //跳过前三条记录开始查询
        employees.stream().skip(3).forEach(System.out::println);

        //4.distinct 筛选,通过流所生成元素的hashcode和equals去除重复元素!
        employees.stream().distinct().forEach(System.out::println);
    }
}

2.2:映射

public class StreamAPITest2 {

    //映射
    @Test
    public void test1(){
        //map(Function f) -接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会应用到每个函数上,并将其映射成一个新的元素!
        List<String> list = Arrays.asList("aa", "bb", "cc");
        list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
        System.out.println();

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

        //练习二: 这里返回的效果,相当于add(Object obj)的效果,[1, 2, 3, [4, 5, 6]] 见test2()
        Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
        streamStream.forEach( s -> {
            s.forEach(System.out::println);
        });
        System.out.println();

        // flatMap(Function f) 接收一个函数作为参数, 将流中的每个值转换成另一个流, 然后把所有流连接成一个流.
        Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::fromStringToStream);
        characterStream.forEach(System.out::println);
    }

    //将字符串中的多个字符构成的集合转换为对应的Stream的实例!
    public static Stream<Character> fromStringToStream(String str){
        List<Character> list = new ArrayList<>();
        for (char c : str.toCharArray()){
            list.add(c);
        }
        return list.stream(); //alt+enter: 快速生成编译时类型和返回值类型是什么!
    }

    @Test
    public void test2(){
        List list1 = new ArrayList(Arrays.asList("1","2","3"));
        List list2 = new ArrayList(Arrays.asList("4","5","6"));

//        list1.add(list2);
//        System.out.println(list1); //[1, 2, 3, [4, 5, 6]]: 这就好比map()的思想!

        list1.addAll(list2);
        System.out.println(list1); // [1, 2, 3, 4, 5, 6] :这就好比flatMap()的思想
    }

}

2.3:排序

public class StreamAPITest3 {

    /**
     * 排序:
     *  1.自然排序 sorted()
     *  2.定制排序 sorted(Comparator com)
     */
    @Test
    public void test1(){
        //自然排序 sorted()
        List<String> list = new ArrayList<>(Arrays.asList("1","3","2","4"));
        list.stream().sorted().forEach(System.out::println);
        System.out.println();

        //定制排序 sorted(Comparator com)
//        List<Employee> employees = EmployeeData.getEmployees();
//        employees.stream().sorted().forEach(System.out::println);
        //cn.itcast.lambda.Employee cannot be cast to java.lang.Comparable: 因为Employee没有实现Comparator接口!

        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().sorted((o1,o2) ->{
            int age = Integer.compare(o1.getAge(),o2.getAge());
            if(age != 0){ //!=0,就说明年龄不相等!
                return age;
            }else{ //年龄相等,就按薪水排序!
                return Double.compare(o1.getSalary(),o2.getSalary());
            }
        }).forEach(System.out::println);
    }
}

三:Stream流的终止操作

3.1:匹配与查找
//小技巧:如果你想不起来该函数式接口的抽象方法,除了点进来自己看,还可以先自己用匿名生成出来的基础去改!
public class StreamStopTest1 {

    //匹配与查找
    @Test
    public void test(){
        /**
         * 1.allMatch(Predicate p) 检查是否匹配所有元素!
         * 练习:是否所有的员工的年龄都>18
         */
        List<Employee> employees = EmployeeData.getEmployees();
        System.out.println(employees.stream().allMatch(e ->  e.getAge() > 18));

        /**
         * 2.anyMatch(Predicate p) 检查是否至少匹配一个元素
         * 练习:是否存在员工的工资>10000
         */
        System.out.println(employees.stream().anyMatch(e -> e.getSalary() > 10000));

        /**
         * 3.noneMatch(Predicate p) 检查是否没有匹配的元素
         * 练习:是否存在员工姓“王”
         */
        System.out.println(employees.stream().noneMatch(e -> e.getName().startsWith("王")));
    }

    @Test
    public void test2(){
        // 3.findFirt() 返回第一个元素(注:可以排完序后返回第一个!)
        List<Employee> employees = EmployeeData.getEmployees();
        Optional<Employee> first = employees.stream().findFirst();
        System.out.println(first);

        // 4.findAny() 返回当前流中任意元素: 使用并行流,而不是串行流!
        Optional<Employee> any = employees.parallelStream().findAny();
        System.out.println(any);
    }

    @Test
    public void test3(){
        //5.count() 返回流中元素的个数
        List<Employee> employees = EmployeeData.getEmployees();
        long count = employees.stream().count();
        System.out.println(count);

        /**
         * 6.max(Comparator c) 返回流中的最大值!
         * 练习:返回最高的工资
         *
         * employees.stream().map(e -> e.getSalary()) 这个e就表示从流中拿出来一个一个的元素!
         * map(e -> e.getSalary()) 映射吗,左边是员工(key), 右边是员工的工资(value)
         */
        Optional<Double> maxSalary = employees.stream().map(e -> e.getSalary()).max(Double::compareTo);
        System.out.println(maxSalary); //Optional[50000.0]

        /**
         * 7.min(Comparator c) 返回流中的最小值!
         * 练习:返回最低的工资
         */
        Optional<Employee> minSalaryEmp = employees.stream().min((o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary()));
        System.out.println(minSalaryEmp); //Optional[Employee(id=1005, name=wangwu, age=26, salary=10000.0)] 因为没做map等映射,所以返回是整条记录!
    }

    @Test
    public void test4(){
        //8.foreach(Consumer c) 内部迭代
        List<Employee> employees = EmployeeData.getEmployees();
        employees.stream().forEach(System.out::println);
        System.out.println();

        //9.使用集合的遍历操作,外部迭代
        employees.forEach(System.out::println);
    }
}
3.2:归约
@Test
public void test1(){
    /**
         * reduce(T identity, BinaryOperator): 可以将流中的元素反复结合起来得到一个值,返回T
         *       identity: 是初始化的值!
         * BinaryOperator接口继承了BiFunction中的抽象方法:R apply(T t, U u);
         *
         * 练习:计算1-10自然数的和!
         */
    List<Integer> nums = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    Integer reduce = nums.stream().reduce(0, new BinaryOperator<Integer>() {
        @Override
        public Integer apply(Integer num1, Integer num2) {
            return num1+num2;
        }
    });

    System.out.println(reduce); //55

    //lambda表达式
    Integer reduce1 = nums.stream().reduce(0, (num1, num2) -> num1 + num2);
    System.out.println(reduce1); //55

    //方法引用: Integer apply(Integer integer, Integer integer2)  结构类似于  int sum(int a, int b)
    Integer reduce2 = nums.stream().reduce(10, Integer::sum);
    System.out.println(reduce2); //65, 因为初始化是10
}

/**
     * reduce(BinaryOperator): 可以将流中的元素反复结合起来得到一个值,返回Optional<T>
     *     
     * 练习:计算员工工资的总和!
     */
@Test
public void test2(){
    List<Employee> employees = EmployeeData.getEmployees();
    Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
    Optional<Double> reduce = salaryStream.reduce(Double::sum);
    System.out.println(reduce);
}

3.3:收集

/**
 *  收集
 */
@Test
public void test1(){
    // collect(Collector c) 将流转换成其他形式,接收一个Collector接口的实现!
    // 练习:查找工资大于6000的员工,结果返回一个list或者set
    List<Employee> employees = EmployeeData.getEmployees();
    List<Employee> list = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
    System.out.println(list);
    System.out.println();

    //返回一个set集合
    Set<Employee> set = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
    System.out.println(set);
}

Collectors的其他方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ttun9yM-1591886055889)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1591885941437.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rYUTX3UM-1591886055890)(C:\Users\wzj\AppData\Roaming\Typora\typora-user-images\1591885895381.png)]

  • 演示一下Collectors中的groupingBy方法
@Data
public class FruitBasket {//水果篮
    private List<Fruit> fruits;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Fruit {//水果

    private String kindId; //种类id
    private String name; //水果
}

public class Demo {

    public static void main(String[] args) {
        //1.
        FruitBasket fruitBasket = new FruitBasket();
        List<Fruit> fruits = new ArrayList<Fruit>();
        fruits.add(new Fruit("1","砂糖橘"));
        fruits.add(new Fruit("1","蜜桔"));
        fruits.add(new Fruit("2","无籽西瓜"));
        fruits.add(new Fruit("2","麒麟西瓜"));
        fruits.add(new Fruit("3","米蕉"));
        fruitBasket.setFruits(fruits);

        Map<String, List<Fruit>> collect = fruitBasket.getFruits().stream().collect(Collectors.groupingBy(fruit -> fruit.getKindId()));
        System.out.println(collect);

        /**
         * {
         * 	1=[Fruit(kindId=1, name=砂糖橘), Fruit(kindId=1, name=蜜桔)],
         * 	2=[Fruit(kindId=2, name=无籽西瓜), Fruit(kindId=2, name=麒麟西瓜)],
         * 	3=[Fruit(kindId=3, name=米蕉)]
         * }
         */
    }
}    

注:肯定不止这些,我列出来的都是比较常见的!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章