一、简介
JDK8中一个很重要的新特性就是Stream流,可以在写代码的时候节省很多的步骤,短短一行就可以完成对集合、数组等数据的筛选过滤和转换。
- Stream是元素的集合,这点让Stream看起来用些类似Iterator;
- 可以支持顺序和并行的对原Stream进行汇聚的操作;
可以把Stream当成一个高级版本的Iterator。原始版本的Iterator,用户只能一个一个的遍历元素并对其执行某些操作;高级版本的Stream,用户只要给出需要对其包含的元素执行什么操作,比如“过滤掉长度大于10的字符串”、“获取每个字符串的首字母”等,具体这些操作如何应用到每个元素上,交给Stream就好了。
二、使用Stream流
2.1 步骤
- 创建Stream
- 中间操作
- 终止操作
2.2 创建Stream
/**
* 1.创建Stream
*/
public static void test1() {
// 1.可以通过Collection系列集合提供的stream() 或 parallelStream() 获取流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
// 2.通过 Arrays 中的静态方法 stream() 获取数组流
Person[] person = new Person[10];
Stream<Person> stream2 = Arrays.stream(person);
// 3.通过 Stream 类中的静态方法 of()
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
// 4.创建无限流
// 迭代
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
// 生成
Stream<Double> stream5 = Stream.generate(Math::random);
}
2.3 中间操作
注意:多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”
用于举例的集合:
public static List<Person> personList = Arrays.asList(
new Person(1, "AA"),
new Person(2, "BB"),
new Person(3, "CC"),
new Person(4, "EE"),
new Person(4, "DD"),
new Person(4, "DD")
);
2.3.1 筛选与切片
filter —— 接收Lambda ,从流中排除某些元素。
// 内部迭代:迭代操作由Stream API 完成 public static void test2(){ Stream<Person> personStream = personList.stream().filter(e -> e.getId() > 3); // 终止操作:一次性处理 personStream.forEach(System.out::println); }
limit —— 截断流,使其元素不超过给定数量。
public static void test3(){ Stream<Person> personStream = personList.stream().limit(2); personStream.forEach(System.out::println); }
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与limit(n)互补
public static void test4(){ Stream<Person> personStream = personList.stream().skip(2); personStream.forEach(System.out::println); }
distinct —— 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
public static void test5(){ Stream<Person> personStream = personList.stream().distinct(); personStream.forEach(System.out::println); }
2.3.2 映射
映射map—接收Lambda ,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
public static void test6(){ List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee"); // 调用String的方法转为大写 list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); // 调用Person的getName方法获取名字 personList.stream().map(Person::getName).forEach(System.out::println); }
flatMap—接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
先写一个方法,该方法将传入的String解析为字符流
public static Stream<Character> filterCharacter(String str) {
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
public static void test7() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
// 如果这里使用map,则获取的是Stream中还有多个Stream,类似于{{a,a,a},{b,b,b}}
Stream<Stream<Character>> stream = list.stream().map(TestStream01::filterCharacter);
// 遍历则需要两层
stream.forEach(e->{
e.forEach(System.out::println);
});
// 使用flatMap,则会直接将多个流连接成一个流,类似于{a,a,a,b,b,b}
Stream<Character> characterStream = list.stream().flatMap(TestStream01::filterCharacter);
// 遍历只需要一层
characterStream.forEach(System.out::println);
}
2.3.3 排序
sorted()——自然排序,按照对象的Comparable排序
public static void test8() { List<String> list = Arrays.asList("ccc", "bbb", "aaa", "eee", "ddd"); list.stream().sorted().forEach(System.out::println); }
sorted(Comparator com)——定制排序,按照传入的Comparator排序
public static void test9() {
personList.stream().sorted((e1, e2) -> {
if (e1.getId().equals(e2.getId())) {
return e1.getName().compareTo(e2.getName());
} else {
return e1.getId().compareTo(e1.getId());
}
}).forEach(System.out::println);
}
2.4 终止操作
用于终止操作举例的集合:
public static List<Employee> employees = Arrays.asList(
new Employee("张三", 18, 9999.99, Status.FREE),
new Employee("李四", 58, 5555.55, Status.BUSY),
new Employee("王五", 26, 3333.33, Status.VOCATION),
new Employee("赵六", 36, 6666.66, Status.FREE),
new Employee("田七", 12, 8888.88, Status.BUSY)
);
2.4.1 查找与匹配
- allMatch——检查是否匹配所有元素
- anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配所有元素
public static void test10() { boolean b1 = employees.stream().allMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b1); boolean b2 = employees.stream().anyMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b2); boolean b3 = employees.stream().noneMatch((e) -> e.getStatus().equals(Status.BUSY)); System.out.println(b3); }
4.findFirst——返回第一个元素
// 此处先以salary进行了排序,然后取出了第一个元素 // 取出的元素被封装在Optional中,用于避免空值,可以通过orElse来设置为空时的值 public static void test11() { Optional<Employee> op = employees.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst(); System.out.println(op.get()); }
findAny—返回当前流中的任意元素
// 先过滤出FREE状态元素,再从中取除符合要求的一个数据(串行流的话,肯定取出第一个符合的元素) public static void test12() { Optional<Employee> any = employees.stream().filter(e -> e.getStatus().equals(Status.FREE)).findAny(); System.out.println(any.get()); }
count——返回流中元素的总个数
- max——返回流中最大值
min——返回流中最小值
public static void test13() { long count = employees.stream().count(); System.out.println(count); Optional<Employee> max = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(max.get()); Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(min.get()); }
2.4.2 归约
reduce(T identity,BinaryOperator) / reduce(BinaryOperator) — 可以将流中元素反复结合起来,得到一个值
public static void test14() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Integer sum = list.stream().reduce(0, Integer::sum); System.out.println(sum); System.out.println("================================="); // 获取employees工资总和 Optional<Double> salary = employees.stream().map(Employee::getSalary).reduce(Double::sum); System.out.println(salary.get()); }
2.4.3 收集
collect — 将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
public static void test15() { // 收集到List中 List<String> nameList = employees.stream().map(Employee::getName).collect(Collectors.toList()); nameList.forEach(System.out::println); System.out.println("==============="); // 收集到Set中,去重 Set<String> nameSet = employees.stream().map(Employee::getName).collect(Collectors.toSet()); nameSet.forEach(System.out::println); System.out.println("==============="); // 收集到特殊的Set中,例如HashSet HashSet<String> nameHashSet = employees.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new)); nameHashSet.forEach(System.out::println); System.out.println("==============="); // 总数 Long count = employees.stream().collect(Collectors.counting()); System.out.println(count); System.out.println("==============="); // 平均值 Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg); System.out.println("==============="); // 总和 Double sum = employees.stream().collect(Collectors.summingDouble(Employee::getSalary)); System.out.println(sum); System.out.println("==============="); // 分组 Map<Status, List<Employee>> map1 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus)); System.out.println(map1); System.out.println("==============="); // 多级分组 Map<Status, Map<String, List<Employee>>> map2 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> { if (e.getAge() <= 35) { return "青年"; } else if (e.getAge() <= 50) { return "中年"; } else { return "老年"; } }))); System.out.println(map2); System.out.println("==============="); // 分区 满足条件一个区,不满足一个区 Map<Boolean, List<Employee>> map3 = employees.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000)); System.out.println(map3); System.out.println("==============="); // 获取DoubleSummaryStatistics,再从中可获取最小值,最大值,平均值,总数,总和 DoubleSummaryStatistics dss = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println(dss.getMax()); System.out.println(dss.getMin()); System.out.println(dss.getAverage()); System.out.println(dss.getCount()); System.out.println(dss.getSum()); // 连接字符串 String nameStr = employees.stream().map(Employee::getName).collect(Collectors.joining(",","===","===")); System.out.println(nameStr); }