1. 创建 Stream

  • Collection.stream
  • Stream.of
  • String.chars
  • IntStream.range

2. Stream 中间操作

返回 stream 的操作都是中间操作。
collect 等

  • 仍然返回 Stream 的操作
  • filter
  • map
  • flatMap 每使用一次,流降低一个维度
  • sorted

3. Stream 终结操作

  • 返回非 stream 的操作,包括返回 void
  • 一个流只能被消费一次
  • collect【最强大的操作】
  • forEach
  • count/max/min
  • findFirst/findAny
  • anyMatch/noneMatch

4. Collector 与 Collectors

Collector 接口顾名思义收集者,把输入流汇聚到一起成为一个 collection 集合。而 Collectors 则是对应的工具类。
一般配合 stream.collect 使用。Collectors 常用方法:

  • toSet/toList/toCollection
  • joining
  • toMap
  • groupingBy

5. 并发流

parallel 并发数是CPU核心数-1,当前CPU核心数是:Runtime.getRuntime().availableProcessors()。

  • parallelStream()
  • 可以通过并发提高互相独立的操作的性能
  • 在正确使用的前提下,可以获得近似线性的性能提升
  • 但是需要进行实际测试,盲目的使用可能导致实际上更慢

6. 实战

6.1 filter/count/sorted/map/toCollection

  1. import java.util.Arrays;
  2. import java.util.Comparator;
  3. import java.util.LinkedList;
  4. import java.util.List;
  5. import java.util.stream.Collectors;
  6. public class Problem1 {
  7. static class User {
  8. private String name;
  9. private int age;
  10. User(String name, int age) {
  11. this.name = name;
  12. this.age = age;
  13. }
  14. public String getName() { return name; }
  15. public int getAge() { return age; }
  16. }
  17. // 编写一个方法,统计"年龄大于等于60的用户中,名字是两个字的用户数量"
  18. public static int countUsers(List<User> users) {
  19. return (int) users.stream()
  20. .filter(user -> user.getAge() >= 60)
  21. .filter(user -> user.getName().length() == 2)
  22. .count();
  23. }
  24. // 编写一个方法,筛选出年龄大于等于60的用户,然后将他们按照年龄从大到小排序,将他们的名字放在一个LinkedList中返回
  25. public static LinkedList<String> collectNames(List<User> users) {
  26. return users.stream()
  27. .filter(user -> user.getAge() >= 60)
  28. .sorted(Comparator.comparing(User::getAge).reversed())
  29. .map(User::getName)
  30. .collect(Collectors.toCollection(LinkedList::new));
  31. }
  32. public static void main(String[] args) {
  33. System.out.println(
  34. countUsers(
  35. Arrays.asList(
  36. new User("张三", 60),
  37. new User("李四", 61),
  38. new User("张三丰", 300),
  39. new User("王五", 12))));
  40. System.out.println(
  41. collectNames(
  42. Arrays.asList(
  43. new User("张三", 60),
  44. new User("李四", 61),
  45. new User("张三丰", 300),
  46. new User("王五", 12))));
  47. }
  48. }

6.2 anyMatch

  1. import java.util.Arrays;
  2. import java.util.List;
  3. public class Problem2 {
  4. // 判断一段文本中是否包含关键词列表中的文本,如果包含任意一个关键词,返回true,否则返回false
  5. // 例如,text="catcatcat,boyboyboy", keywords=["boy", "girl"],返回true
  6. // 例如,text="I am a boy", keywords=["cat", "dog"],返回false
  7. public static boolean containsKeyword(String text, List<String> keywords) {
  8. // return keywords.stream().filter(text::contains).findAny().isPresent(); // 笨办法
  9. return keywords.stream().anyMatch(text::contains);
  10. }
  11. public static void main(String[] args) {
  12. System.out.println(containsKeyword("catcatcat,boyboyboy", Arrays.asList("boy", "girl")));
  13. System.out.println(containsKeyword("I am a boy", Arrays.asList("cat", "dog")));
  14. }
  15. }

6.3 String.chars/filter/count

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

6.4 orted/groupingBy

  1. import java.util.Arrays;
  2. import java.util.Comparator;
  3. import java.util.List;
  4. import java.util.Map;
  5. import java.util.Objects;
  6. import java.util.stream.Collectors;
  7. public class Problem4 {
  8. // 再用流的方法把之前的题目做一遍吧:
  9. // 请编写一个方法,对传入的List<Employee>进行如下处理:
  10. // 返回一个从部门名到这个部门的所有用户的映射。同一个部门的用户按照年龄进行从小到大排序。
  11. // 例如,传入的employees是[{name=张三, department=技术部, age=40 }, {name=李四, department=技术部, age=30 },
  12. // {name=王五, department=市场部, age=40 }]
  13. // 返回如下映射:
  14. // 技术部 -> [{name=李四, department=技术部, age=30 }, {name=张三, department=技术部, age=40 }]
  15. // 市场部 -> [{name=王五, department=市场部, age=40 }]
  16. public static Map<String, List<Employee>> collect(List<Employee> employees) {
  17. return employees.stream()
  18. .sorted(Comparator.comparing(Employee::getAge))
  19. .collect(Collectors.groupingBy(Employee::getDepartment));
  20. }
  21. public static void main(String[] args) {
  22. System.out.println(
  23. collect(
  24. Arrays.asList(
  25. new Employee(1, "张三", 40, "技术部"),
  26. new Employee(2, "李四", 30, "技术部"),
  27. new Employee(3, "王五", 40, "市场部"))));
  28. }
  29. static class Employee {
  30. // 用户的id
  31. private final Integer id;
  32. // 用户的姓名
  33. private final String name;
  34. // 用户的年龄
  35. private final int age;
  36. // 用户的部门,例如"技术部"/"市场部"
  37. private final String department;
  38. Employee(Integer id, String name, int age, String department) {
  39. this.id = id;
  40. this.name = name;
  41. this.age = age;
  42. this.department = department;
  43. }
  44. @Override
  45. public String toString() {
  46. return "Employee{" +
  47. "id=" + id +
  48. ", name='" + name + '\'' +
  49. ", age=" + age +
  50. ", department='" + department + '\'' +
  51. '}';
  52. }
  53. public Integer getId() { return id; }
  54. public String getName() { return name; }
  55. public int getAge() { return age; }
  56. public String getDepartment() { return department; }
  57. @Override
  58. public boolean equals(Object o) {
  59. if (this == o) {
  60. return true;
  61. }
  62. if (o == null || getClass() != o.getClass()) {
  63. return false;
  64. }
  65. Employee person = (Employee) o;
  66. return Objects.equals(id, person.id);
  67. }
  68. @Override
  69. public int hashCode() {
  70. return Objects.hash(id);
  71. }
  72. }
  73. }

6.5 toMap

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) {
        return orders.stream()
                .collect(Collectors.toMap(Order::getId, order -> order));
    }

    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; }

        @Override
        public String toString() {
            return "Order{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
}

6.6 filter/joining

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) {
        return words.stream()
                .filter(word -> word.length() == 1)
                .collect(Collectors.joining(","));
    }

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

7. 参考

  • Effective Java Item 42-48
  • IDEA stream 调试器

image.png