Stream API

Stream API 主要操作:

筛选与切片

filter(Predicate)   筛选元素,从流中排除不满足Predicate的某些元素
limit(n)            截断流,使其元素不超过给定数量
skip(n)             跳过前面n个元素,若元素不足n个,则返回空流
distinct()          去重,通过流所生成元素的hashCode()与equals()去除重复元素

实体类

public class User {

    private String name;  // 姓名
    private String age;  // 年龄
    private double salary; // 薪水
    private Status status; // 工作状态:FREE 空闲, BUSY 繁忙 INVOCATION 休假

    public enum Status {
        BUSY, FREE, INVOCATION;
    }
    
    public User(){
        
    }

    public User(String name, String age, double salary, Status status) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.status = status;
    }
    
    // Getter Setter equals hashCode toString
    
}
         List<User> list = Arrays.asList(
                new User("张三", 21, 6000, User.Status.BUSY),
                new User("李四", 23, 7000, User.Status.FREE),
                new User("王五", 25, 6000, User.Status.INVOCATION),
                new User("赵六", 21, 10000, User.Status.BUSY),
                new User("赵六", 21, 10000, User.Status.BUSY)
        );

        // filter 筛选出BUSY状态的员工
        list.stream()
                .filter(u -> User.Status.BUSY.equals(u.getStatus()))
                .forEach(System.out::println);
                
        /*  结果:
            User{name='张三', age='21', salary=6000.0, status=BUSY}
            User{name='赵六', age='21', salary=10000.0, status=BUSY}
            User{name='赵六', age='21', salary=10000.0, status=BUSY}      
        */  

        // 筛选出工资等于6000的一个员工
        list.stream()
                .filter(u -> u.getSalary() == 6000)
                .limit(1)
                .forEach(System.out::println);
                
        /*  结果:
            User{name='张三', age='21', salary=6000.0, status=BUSY}   
        */  


        // 筛选工资等于6000,不是第一个员工的员工
        list.stream()
                .filter(u -> u.getSalary() == 6000)
                .skip(1)
                .forEach(System.out::println);
                
        /*  结果:
            User{name='王五', age='25', salary=6000.0, status=INVOCATION} 
        */          

        // 员工列表去重
        list.stream()
                .distinct()
                .forEach(System.out::println);
                
        /*  结果:
            User{name='张三', age='21', salary=6000.0, status=BUSY}
            User{name='李四', age='23', salary=7000.0, status=FREE}
            User{name='王五', age='25', salary=6000.0, status=INVOCATION}
            User{name='赵六', age='21', salary=10000.0, status=BUSY}
        */        

映射

map(Function)       提取元素,使用Function应用到每个元素
flatMap(Function>)   提取元素,应用每个元素得到每一个新的Stream,然后将这些Stream合并成新的Stream
    public static void test() {
        List<String> list = Arrays.asList("abc", "def", "ghi");

        // 将每个元素(这里指的每个元素是指"abc", "def"这种),转换成大写
        list.stream()
                .map(String::toUpperCase)
                .forEach(System.out::print);
        
        /*  结果:
            ABCDEFGHI
        */            

        // 提取所有字符
        // 步骤:应用每个元素,比如"abc"将其转换成Stream,然后得到三个Stream,最终合并到一个新的Stream,类似于list.addAll
        list.stream()
                .flatMap(Test::strToCharator)  // ==> .flatMap(s -> strToCharator(s))
                .forEach(System.out::print);    
                        
        /*  结果:
            abcdefghi
        */ 
    }

    public static Stream<Character> strToCharator(String s){
        List<Character> list = new ArrayList<>();
        for (char c : s.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }        

重点说下区别:

Stream<Stream<Character>> characterStream = list.stream()
        .map(Test::strToCharator);
characterStream.forEach(s -> s.forEach(System.out::println));
// s = Stream ,所有需要再迭代遍历一次,共两次   
// 变化为 "abc","bcd", "ghi" --> {a, b, c}, {b, c, d}, {g, h , i} --> {  {a, b, c}, {b, c, d}, {g, h , i}  }


Stream<Character> characterStream2 = list.stream()
        .flatMap(Test::strToCharator);
characterStream2.forEach(System.out::println);
// 变化为 "abc","bcd", "ghi" -->  {a, b, c }, {b, c, d}, {g, h , i} -->  {a,b,c,d,e,f,g,h,i}


类比于List list1, List list2
list1.add(list2)     // 将list2作为list1的元素
list1.addAll(list2)  // 将list2中所有元素放入到list1中

排序

sorted()                自然排序
sorted(Comparator)      自定义排序
        List<String> list = Arrays.asList("bcd", "abe", "afa");

        // 自然排序
        list.stream()
                .sorted()
                .forEach(System.out::println);

        // 按照string排序
        list.stream()
                .sorted(String::compareTo)
                .forEach(System.out::println);

        // 按照第二个元素排序        
        list.stream()
                .sorted((s1, s2) -> Character.valueOf(s1.charAt(1)).compareTo( Character.valueOf(s2.charAt(1))))
                .forEach(System.out::println);

        // 等于上面,使用工具类  Comparator 更加方便      
        list.stream()
                .sorted(Comparator.comparing(s -> s.charAt(1)))
                .forEach(System.out::println);

查找与匹配

allMatch        流中所有元素都匹配成功,返回true
anyMatch        流中匹配一个元素即返回true
noneMatch       流中所有元素都不匹配,返回true,与allMatch相反
findFirst       返回流中匹配的第一个元素
findAny         返回流中匹配到的第一个元素,由于可能是parallelStream并行方式,所以不等于findFirst
count           返回流中元素个数
max             返回流中最大值
min             返回流中最小值
        List<User> list = Arrays.asList(
                new User("张三", 21, 6000, User.Status.BUSY),
                new User("李四", 23, 7000, User.Status.FREE),
                new User("王五", 25, 6000, User.Status.INVOCATION),
                new User("赵六", 21, 10000, User.Status.BUSY)
        );

        // 1. 所有员工是否都处于BUSY状态
        boolean b = list.stream().allMatch(u -> User.Status.BUSY.equals(u.getStatus()));  // ==> false
        // 更好的方式: list.stream().map(User::getStatus).allMatch(User.Status.BUSY::equals)

        // 2. 至少有一个员工处理BUSY状态
        boolean b2 = list.stream().map(User::getStatus).anyMatch(User.Status.BUSY::equals);  // ==> true

        // 3. 所有员工都不处于BUSY状态
        boolean b3 = list.stream().map(User::getStatus).noneMatch(User.Status.BUSY::equals);  // ==> false

        // 4. 找出处于BUSY状态的第一个员工
        Optional<User> firstUser = list.stream().filter(u -> User.Status.BUSY.equals(u.getStatus())).findFirst();  // firstUser.get() 张三

        // 5. 找出任意一个处于BUSY状态的员工
        Optional<User> anyUser = list.parallelStream().filter(u -> User.Status.BUSY.equals(u.getStatus())).findAny(); // anyUser.get() 赵六

        // 6. 处于BUSY状态的员工数量
        long count = list.stream().map(User::getStatus).filter(User.Status.BUSY::equals).count();  // ==> 2

        // 7. 找到工资最高的员工
        Optional<User> maxSalaryUser = list.stream().max(Comparator.comparingDouble(User::getSalary)); // maxSalaryUser.get() 赵六

        // 8. 找到年龄最小的员工
        Optional<User> minAgeUser = list.stream().min(Comparator.comparingInt(User::getAge)); // minAgeUser.get() 张三

归约与收集

reduce(T identity, BinaryOperator) / reduce(BinaryOperator)   可以将流中的元素反复结合起来,得到一个值
collect         将流转换成其他形式,接收

BinaryOperator extends BiFunction 所以是 T, T, R, 两个参数,一个返回值

reduce
        List<User> list = Arrays.asList(
                new User("张三", 21, 6000, User.Status.BUSY),
                new User("李四", 23, 7000, User.Status.FREE),
                new User("王五", 25, 6000, User.Status.INVOCATION),
                new User("赵六", 21, 10000, User.Status.BUSY)
        );

        int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        // 1. 数组所有元素求和
        int sum = Arrays.stream(arr).reduce(0, (x, y) -> x + y); // 55
        // 说明:这里identity=0,指代第一次运算的x=0,y=数组第一个元素,也就是1,如果identity=2,那么第一次就是x=2, y=1, 那么最终结果=56
        // 由于有第一次运算默认值,所以这里一定有返回值,所以设计为返回int,假设没有初始值,也就是下面这种,不确定元素是否足够,返回Optional

        OptionalInt optionalInt = Arrays.stream(arr).reduce((x, y) -> x + y); // optionalInt.getAsInt() == 55
        // OptionalInt 为 Optional 专门为int的包装类,比Optional性能更好,减少装箱拆箱操作

        // 2. 计算所有员工工资总和
        Optional<Double> sumSalary = list.stream().map(User::getSalary).reduce(Double::sum); // sumSalary.get()

map-reduce 这种思想应用很广,比如hadoop,提取然后计算

collect

        List<User> list = Arrays.asList(
                new User("张三", 21, 6000, User.Status.BUSY),
                new User("李四", 23, 7000, User.Status.FREE),
                new User("王五", 25, 6000, User.Status.INVOCATION),
                new User("赵六", 21, 10000, User.Status.BUSY)
        );
        
                // ---------------------- 转换为集合 -------------------
        // collect(Collector),可以工具类Collectors生成Collector
        // 1. 获取员工姓名列表
        // 返回一个list
        List<String> userNameList = list.stream().map(User::getName).collect(Collectors.toList());
        // 返回set
        Set<String> userNameSet = list.stream().map(User::getName).collect(Collectors.toSet());
        // 返回hashSet
        HashSet<String> userNameHashSet = list.stream().map(User::getName).collect(Collectors.toCollection(HashSet::new));

        
        // ---------------------- 计算 ------------------------
        // 2. 得到员工数量
        // 总数
        Long count = list.stream().collect(Collectors.counting()); // == list.stream().count()
        // 工资大于5000员工数量
        Long salaryConditionCount = list.stream().map(User::getSalary).filter(s -> s > 5000).collect(Collectors.counting()); // == filter().count()

        // 3. 所有员工平均工资
        Double averagSalary = list.stream().collect(Collectors.averagingDouble(User::getSalary));
        // 4. 最高/最低工资
        Optional<Double> maxSalary = list.stream().map(User::getSalary).collect(Collectors.maxBy(Double::compare)); // == map().max()


        // -------------------- 分组 --------------------------
        // 类似于SQL中的group by
        // 5. 根据工作状态分组 (单级分组) { BUSY: List, FREE: List }
        Map<User.Status, List<User>> userMapByStatus = list.stream().collect(Collectors.groupingBy(User::getStatus));
        // 6. 先根据工作状态分组,再根据年龄分组 (多级分组){ BUSY: {21 : List, 23 : List}, FREE : { 25 : List, 26 : List } }
        Map<User.Status, Map<Integer, List<User>>> userMapByStatusAndAge = list.stream()
                .collect(Collectors.groupingBy(User::getStatus, Collectors.groupingBy(User::getAge)));
        // 7. 更多级分组
        Map<User.Status, Map<Integer, Map<Double, List<User>>>> userMapByStatusAndAgeAndSalary = list.stream()
                .collect(Collectors.groupingBy(User::getStatus, Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getSalary))));
        // 8. 自定义分组,先按工作状态分组,再自定义下一次分组key,比如小于30岁,key为青年,大于30岁,key为中年
        // ==> { BUSY : { "青年" : List, "老年" : List }  }
        list.stream().collect(Collectors.groupingBy(User::getStatus, Collectors.groupingBy(u -> {
            User user = (User)u;
            if (user.getAge() < 30) {
                return "青年";
            } else {
                return "中年";
            }
        })));

        
        // ------------------ 分区 ------------------------
        // 特殊情况下的分组形式,分组条件结果为true/false,即 { true : List, false : List }
        // 9. 将工资大于6000的分一个区,小于等的分一个区 ==> {true : List<工资大于6000的User>, false : List<工资小于等于6000的User> }
        Map<Boolean, List<User>> partitionSalaryMap = list.stream().collect(Collectors.partitioningBy(u -> u.getSalary() > 6000));


        // ------------------ 统计 -----------------------
        // 返回一个统计结果相关的操作类,可用来得到最大值,最小值等等,当要获取一个集合的各项情况时,这种方式比较好,必须要生成多次流操作获取
        DoubleSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingDouble(User::getSalary));
        double summaryStatisticsSum = summaryStatistics.getSum();  // 总和,工资总数
        long summaryStatisticsCount = summaryStatistics.getCount(); // 总的个数,员工数
        double summaryStatisticsMax = summaryStatistics.getMax();  // 最高工资
        double summaryStatisticsMin = summaryStatistics.getMin(); // 最低工资
        double summaryStatisticsAverage = summaryStatistics.getAverage(); // 平均工资

你可能感兴趣的:(Java8)