32中级 - Java 8 Stream

109 阅读5分钟

java8最重要的api

  • Stream

什么是流

1.png

流的方法

  • 创建流,Collection.stream();
  • java8之前的解决需求的案例
package com.github.hcsp.functional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {
    static class User {
        private String name;
        private int age;
    }

    public static void main(String[] args) {
        // 请把姓张的用户跳出来,把他们按照年龄排序,然后把他们的名单报给我
        List<User> users = getUsers();
        List<User> zhangUsers = new ArrayList<>();
        for (User user : users) {
            if (user.name.startsWith("张")) {
                zhangUsers.add(user);
            }
        }

        Collections.sort(zhangUsers, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                if (o1.age - o2.age < 0) {
                    return -1;
                } else if (o1.age - o2.age > 0) {
                    return 1;
                } else {
                    return 0;
                }
            }
        });
        List<String> names = new ArrayList<>();
        for (User user:zhangUsers) {
            names.add(user.name);
        }
    }

    private static List<User> getUsers() {
        return new ArrayList<>();
    }

    public boolean isSurnameZhang() {
        return this.getName().startsWith("张")
    }
}

  • 流的方式的写法
public static void main(String[] args) {
        // 请把姓张的用户跳出来,把他们按照年龄排序,然后把他们的名单报给我
        users.stream()
                .filter(Main::isZhangUser)
                .sorted(comparing(User::getAge))
                .map(User::getName)
                .collect(toList());
        //也可以
        users.stream()
                .filter(User::isSurnameZhang)
                .sorted(comparing(User::getAge))
                .map(User::getName)
                .collect(toList());
                
    }

    public static int countUpperCaseLetters(String str) {
        return (int) str.chars().filter(Character::isUpperCase).count();
    }

    public static boolean isZhangUser(User user) {
        // 1. 这个方法是有名字的
        // 2. 这个方法可以写非常复杂的代码
        return user.getName().startsWith("张");
    }

private的访问限定

  • 当前的编译单元 compilation unit (文件)

map就是映射

终结操作

  • 返回不是Stream的东西

流调试器

  • 搜索插件stream,全名叫java stream debugger
  public static void printOddNumbersBetween(int start, int end) {
        IntStream.range(start, end + 1)
                .filter(i -> i % 2 != 0)
                .mapToObj(i -> i + ",")
                .forEach(System.out::println);
    }

Stream的API详解

Collection.stream()
Stream.of
String.chars()
IntStream.range()

Stream中结操作

  • 返回Stream的操作,包括void
  • 一个流只能被消费一次
  • forEach
  • count/max/min
  • findFirst/FindAny
  • anyMatch/noneMatch
  • collect
// anyMatch案例
 public static void main(String[] args) {
        List<User> users = Arrays.asList(
                new User("张三", 20),
                new User("张三疯", 15),
                new User("李四", 100)
        );

        boolean anyMatch = users
                .stream()
                .anyMatch(user -> user.getName().
                startsWith("张"));

 }
  • collect案例
  List<String> list = Arrays.asList("I am a boy", "I have a dog", "I am a girl");
        list
                .stream()
                .map(s -> s.split(" "))
                .flatMap(Stream::of)
                .collect(Collectors.toList());

  • optional例子
    • optional一般的例子只当作返回值,不是当作空指针传来传去
public static void main(String[] args) throws IllegalAccessException {
        List<User> users = Arrays.asList(
                new User("张三", 20),
                new User("张三疯", 15),
                new User("李四", 100)
        );

 
//        正确的用法,链式链接

        User user = users
                .stream()
                .filter(User::isSurnameZhang)
                .findAny().orElseThrow(IllegalAccessError::new);

        System.out.println(user.getName());



        // 错误用法,当成Null

        Optional<User> optionalUser = users
                .stream()
                .filter(User::isSurnameZhang)
                .findAny();
        
        if (optionalUser.isPresent()) {
            System.out.println(optionalUser.get().getName());
        } else {
            throw new IllegalAccessException();
        }
    }

collect是最强大的操作

  • 使用collect将流收集起来
users.stream()
    .filter(User::isSurnameZhang)
    .sorted(comparing(User::getAge))
    .map(User::getName)
    .collect(toList());
  • 一般而言不需要重写collects
  • 最常见的是toList(),次常见的是toSet()
  • toCollection,toSet默认是hashSet,如果想要用treeSet
TreeSet<String> result = users
                .stream()
                .filter(User::isSurnameZhang)
                .sorted(comparing(User::getAge))
                .map(User::getName)
                .collect(Collectors.toCollection(TreeSet::new));
  • collectors的特殊用法
  • 案例,按照部门分组
return users.stream().collect(Collectors.groupingBy(User::getDepartment))

并发流

  • 通过并发提升互相之间独立的操作的性能
 public static void main(String[] args) throws IllegalAccessException {
        long t0 = System.currentTimeMillis();
        IntStream.range(1, 100_0000).filter(Primes::isPrime).count();
        System.out.println(System.currentTimeMillis() - t0);

        long t1 = System.currentTimeMillis();
        IntStream.range(1, 100_0000).parallel().filter(Primes::isPrime).count();
        System.out.println(System.currentTimeMillis() - t1);
 }
  • parallelStream()
  • 可以通过兵法提高互相独立的操作的性能
  • 在正确使用的前提下,可以获得近似线性的性能提升
  • 但是要小心,性能要测试,如果你不知道自己在做什么,就不要用
  • 并发度,计算机可以用的几何,默认并发度就是核数-1
  • 推荐读effective java 42-48

课后练习题

  • 1-1
package com.github.hcsp.stream;

import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

public class Problem1 {
    static class User {
        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }

        private String name;
        private int age;

        User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

    // 编写一个方法,统计"年龄大于等于60的用户中,名字是两个字的用户数量"
    public static int countUsers(List<User> users) {
        return (int) users
                .stream()
                .filter(user-> user.getAge()>=60)
                .filter(user-> user.getName().length()==2)
                .count();
    }

    // 编写一个方法,筛选出年龄大于等于60的用户,然后将他们按照年龄从大到小排序,将他们的名字放在一个LinkedList中返回
    public static LinkedList<String> collectNames(List<User> users) {
        LinkedList<String> tempLinkedList = new LinkedList<>();
        tempLinkedList.addAll( users.stream()
                .filter(user-> user.getAge()>=60)
                .sorted(Comparator.comparing(User::getAge).reversed())
                .map(User::getName)
                .collect(Collectors.toList()));
        return tempLinkedList;
    }

    public static void main(String[] args) {
        System.out.println(
                countUsers(
                        Arrays.asList(
                                new User("张三", 60),
                                new User("李四", 61),
                                new User("张三丰", 300),
                                new User("王五", 12))));

        System.out.println(
                collectNames(
                        Arrays.asList(
                                new User("张三", 60),
                                new User("李四", 61),
                                new User("张三丰", 300),
                                new User("王五", 12))));
    }
}
  • 1-2
package com.github.hcsp.stream;

import java.util.Arrays;
import java.util.List;

public class Problem2 {
    // 判断一段文本中是否包含关键词列表中的文本,如果包含任意一个关键词,返回true,否则返回false
    // 例如,text="catcatcat,boyboyboy", keywords=["boy", "girl"],返回true
    // 例如,text="I am a boy", keywords=["cat", "dog"],返回false
    public static boolean containsKeyword(String text, List<String> keywords) {
        return keywords.stream()
                .anyMatch(item -> text.contains(item));
    }

    public static void main(String[] args) {
        System.out.println(containsKeyword("catcatcat,boyboyboy", Arrays.asList("boy", "girl")));
        System.out.println(containsKeyword("I am a boy", Arrays.asList("cat", "dog")));
    }
}
  • 1-3
package com.github.hcsp.stream;

public class Problem3 {
    // 使用流的方法,再把之前的题目做一遍吧
    // 统计一个给定的字符串中,大写英文字母(A,B,C,...,Z)出现的次数。
    // 例如,给定字符串"AaBbCc1234ABC",返回6,因为该字符串中出现了6次大写英文字母ABCABC
    public static int countUpperCaseLetters(String str) {
        return (int) str.chars()
                .filter(Character::isUpperCase)
                .count();
    }

    public static void main(String[] args) {
        System.out.println(countUpperCaseLetters("AaBbCc1234ABC"));
    }
}
  • 1-4
package com.github.hcsp.stream;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.groupingBy;

public class Problem4 {
    // 再用流的方法把之前的题目做一遍吧:
    // 请编写一个方法,对传入的List<Employee>进行如下处理:
    // 返回一个从部门名到这个部门的所有用户的映射。同一个部门的用户按照年龄进行从小到大排序。
    // 例如,传入的employees是[{name=张三, department=技术部, age=40 }, {name=李四, department=技术部, age=30 },
    // {name=王五, department=市场部, age=40 }]
    // 返回如下映射:
    //    技术部 -> [{name=李四, department=技术部, age=30 }, {name=张三, department=技术部, age=40 }]
    //    市场部 -> [{name=王五, department=市场部, age=40 }]
    public static Map<String, List<Employee>> collect(List<Employee> employees) {
        return employees
                .stream()
                .sorted(comparing(Employee::getAge))
                .collect(groupingBy(Employee::getDepartment));
    }

    public static void main(String[] args) {
        System.out.println(
                collect(
                        Arrays.asList(
                                new Employee(1, "张三", 40, "技术部"),
                                new Employee(2, "李四", 30, "技术部"),
                                new Employee(3, "王五", 40, "市场部"))));
    }

    static class Employee {
        // 用户的id
        private final Integer id;
        // 用户的姓名
        private final String name;
        // 用户的年龄
        private final int age;
        // 用户的部门,例如"技术部"/"市场部"
        private final String department;

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

        public Integer getId() {
            return id;
        }

        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }

        public String getDepartment() {
            return department;
        }

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

        @Override
        public int hashCode() {
            return Objects.hash(id);
        }
    }
}
  • 1-5
package com.github.hcsp.stream;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Problem5 {
    // 使用流的方法,把订单处理成ID->订单的映射
    // 例如,传入参数[{id=1,name='肥皂'},{id=2,name='牙刷'}]
    // 返回一个映射{1->Order(1,'肥皂'),2->Order(2,'牙刷')}
    public static Map<Integer, Order> toMap(List<Order> orders) {

        Map<Integer, Order> result =
                orders.stream().collect(Collectors.toMap(Order::getId,
                        item -> item));
        return result;
    }

    public static void main(String[] args) {
        System.out.println(toMap(Arrays.asList(new Order(1, "肥皂"), new Order(2, "牙刷"))));
    }

    static class Order {
        private Integer id;
        private String name;

        Order(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        public Integer getId() {
            return id;
        }

        public String getName() {
            return name;
        }
    }
}
  • 1-6
package com.github.hcsp.stream;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;

public class Problem6 {
    // 使用流的方法,把所有长度等于1的单词挑出来,然后用逗号连接起来
    // 例如,传入参数words=['a','bb','ccc','d','e']
    // 返回字符串a,d,e
    public static String filterThenConcat(Set<String> words) {
        String result = words.stream()
                .filter(item -> item.length() == 1)
                .collect(Collectors.joining(","));
        return result;
    }

    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>(Arrays.asList("a", "bb", "ccc", "d", "e"));
        System.out.println(filterThenConcat(set));
    }
}