上一篇介绍了Lambda的学习,如果对Lambda表达式还不清晰的同学可以戳一下这个链接:java8新特性-lambda表达式入门学习。
java8除了提供了Lambda表达式,操作集合的Stream API也是非常新特性中最值得学习和掌握的,它大大简化了,我们操作数据集合的代码量的书写。
简单来说Stream是一个抽象概念,可以通过查找,过滤,映射等操作,这一点与Scala中集合操作很类似。
Stream是什么?
通俗的说就是操作数据集合的一种手段,你可以使用它,以获取所需要的集合数据源类型,如下图所示:
通常Stream流操作整个流程是创建流对象->对流操作->获得目标数据源操作
创建Stream
通过Collection接口提供的Stream
1.返回一个顺序流
default Streamstream() { return StreamSupport.stream(spliterator(), false); }
2.返回一个并行流
default StreamparallelStream() { return StreamSupport.stream(spliterator(), true); }
那我们简单演示一下这两个流的使用。
Emplyee的基本实体类
package com.codegeek.lambda; import lombok.*; @Setter @Getter @NoArgsConstructor @ToString public class Employee { /** * 员工姓名 */ private String name; /** * 员工年龄 */ private int age; /** * 基本薪水 */ private double basicSalary; /** * 订单成交总额 */ private double dealTotalPrice; public Employee(String name, int age, double basicSalary,double dealTotalPrice) { this.name = name; this.age = age; this.basicSalary = basicSalary; this.dealTotalPrice = dealTotalPrice; } /** * 员工总薪资=基本薪资+提成薪资 * * @return Double */ public Double getTotalSalary() { return this.basicSalary + this.dealTotalPrice * 0.04; } }
测试方法如下:
@Test public void test() { Employee qingLong = new Employee("青龙", 25, 5500, 7500); Employee baiHu = new Employee("白虎", 27, 5000, 9000); Employee zhuQue = new Employee("朱雀", 22, 3800, 4500); Employee xuanWu = new Employee("玄武", 24, 3300, 3300); Listemployees = Arrays.asList(qingLong, baiHu, zhuQue, xuanWu); // 得到一个顺序流,并获取工资大与4000的员工的姓名 Stream stream = employees.stream(); stream.filter(e-> e.getTotalSalary()>4000).map(Employee::getName).forEach(System.out::println); // 得到一个并行流,获取年龄大于25的员工姓名 Stream employeeStream = employees.parallelStream(); employeeStream.filter(employee -> employee.getAge()> 25).map(Employee::getName).forEach(System.out::println ); }
通过Arrays创建Stream流
注意数组里面是什么类型的数组,就会产生同类型的流。
1.产生IntStream
public static IntStream stream(int[] array) { return stream(array, 0, array.length); }
2.产生LongStream
public static LongStream stream(long[] array) { return stream(array, 0, array.length); }
3.产生DoubleStream
public static DoubleStream stream(double[] array) { return stream(array, 0, array.length); }
下面简单演示以下IntStream的使用案列
// 初始化一个数组对象 int[] arr = {11, 55, 44, 20, 45, 16}; // 通过Arrays创建流对象是IntStream Arrays.stream(arr).sorted().forEach(System.out::println);
Stream常见的操作
上面简单介绍了下,流的创建,当流创建好后,我们又该如何使用呢,常见流的操作如下?
- 过滤和切片
方法 | 方法介绍 |
---|---|
filter(Predicate super T> predicate) | 接收断言接口,并从流中排除元素 |
distinct() | 去除流中重复的元素 |
limit(long maxSize) | 截取流中元素个数,类似sql查询limit |
skip(long n) | 跳过元素,跳过前n个元素 |
使用演示:
@Test public void testFilter() { int[] age = {11, 22, 44, 22, 24, 24, 66, 77, 77, 25, 34}; // 使用filter过滤获得大于33的数组元素 Arrays.stream(age).filter(i -> i > 33).forEach(System.out::println); // 去重 Arrays.stream(age).distinct().forEach(System.out::println); // 截取3个元素 Arrays.stream(age).limit(3).forEach(System.out::println); // 跳过前3个元素 Arrays.stream(age).skip(3).forEach(System.out::println); }
- 映射
方法 | 方法介绍 |
---|---|
map(Function mapper) | 接收一个函数式接口,将会映射到流中的每一个元素 |
mapToDouble(ToDoubleFunction mapper) | 接收函数式接口,将映射产生DoubleStream |
mapToLong(ToLongFunction mapper) | 接收函数式接口,将映射产生LongStream |
mapToInt(ToIntFunction mapper) | 接收函数式接口,将映射产生IntStream |
flatMap(Function extends Stream mapper) | 接收函数,将流中的每个值都转换一个流,然后将这些流汇成一个流 |
使用演示:
String[] arr = {"java", "scala", "php", "python", "c++"}; // 将流中的每一个元素转换成大写 Arrays.stream(arr).map(String::toUpperCase).forEach(System.out::println); //将流中的数据转Double类型 long[] array = {1, 4, 6, 7, 12}; // 返回Double类型的Stream Arrays.stream(array).mapToDouble(e-> e* 100).forEach(System.out::println); // 返回Long类型的Stream Arrays.stream(array).mapToLong(e -> e + 23).forEach(System.out::println); // flatMap演示 List> database = new ArrayList<>(); List
noSql = Arrays.asList("redis", "hbase", "membercache"); List sql = Arrays.asList("mysql", "oracle", "db2"); database.add(noSql); database.add(sql); List h = database.stream().flatMap(s -> s.stream().filter(si -> si.contains("h"))).collect(Collectors.toList()); h.stream().forEach(System.out::println);
- 排序
- 排序相对简单,以之前定义的employee类如下:
Employee qingLong = new Employee("青龙", 25, 5500, 7500); Employee baiHu = new Employee("白虎", 27, 5000, 9000); Employee zhuQue = new Employee("朱雀", 22, 3800, 4500); Employee xuanWu = new Employee("玄武", 24, 3300, 3300); Listemployees = Arrays.asList(qingLong, baiHu, zhuQue, xuanWu); // 按照薪水的大小进行排序 employees.stream().sorted(Comparator.comparing(Employee::getTotalSalary)).forEach(System.out::println);
- 查找与匹配
方法 | 方法介绍 |
---|---|
allMatch(Predicate p) | 检查流中的元素是否都匹配 |
anyMatch(Predicate p) | 检查是否匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回流中任意元素 |
count | 返回流中的个数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
方法演示:以初始化的员工类
// 判断所有的员工年龄是否大于18 boolean b = employees.stream().allMatch(e -> e.getAge() > 18); System.out.println("allMatch="+b);// 结果为true // 判断所有员工中有没有年龄大于35的 boolean b1 = employees.stream().anyMatch(e -> e.getAge() > 35); System.out.println("anyMath=" + b1); // 结果为false // 判断所有员工中没有年龄大于35的 boolean b1 = employees.stream().noneMatch(e -> e.getAge() > 35); System.out.println("anyMath=" + b1); // 结果为true // 返回第一个员工的信息 Optionalfirst = employees.stream().findFirst(); System.out.println(first.get()); // 统计年龄大于20的员工个数 ong count = employees.stream().filter(e -> e.getAge() > 20).count(); System.out.println("count="+count); // 统计集合中员工薪资最高的员工信息 Optional max = employees.stream().max(Comparator.comparing(Employee::getTotalSalary)); System.out.println("max=" + max);
- 归约
方法 | 方法介绍 |
---|---|
reduce(BinaryOperator p) | 将流中的元素反复结合起来得到一个值返回Optional |
reduce(T iden,BinaryOperator p) | 将流中的元素反复结合起来得到一个值T |
演示:
Optionalreduce = employees.stream().map(Employee::getTotalSalary).reduce(Double::sum); double v = reduce.get(); System.out.println("reduce="+v); int[] array = {1, 4, 6, 7, 12}; System.out.println("===="); // 这里第一次将0作为x的值然后数组中1作为y,然后计算后的结果是1,第二次将1作为x的值,然后数组中的4作为y值进行相加,后面以此类推,直到将所有的值都进行相加 int reduce = Arrays.stream(array).reduce(0, (x, y) -> x + y); System.out.println("reduce=" + reduce);
- 收集
方法 | 方法介绍 |
---|---|
collect(Collector c) | 将流转换为其他形式,接收Collector接口实现。 |
下面将对Stream做一个总体的回顾和使用。
@Test public void test() { // 避免空指针异常 Optionaloptional = employees.stream().sorted((e1, e2) -> e1.getTotalSalary().compareTo(e2.getTotalSalary())).findFirst(); // 若空指针异常就怎么处理 optional.orElse(new Employee()); System.out.println(optional); // 返回任意一个(并行开启多个线程查找) Optional any = employees.parallelStream().filter((e) -> e.getAge() > 25).findAny(); System.out.println(any); // Max(Comparator按年龄比较) Optional max = employees.stream().max(Comparator.comparing((e) -> e.getAge())); System.out.println(max); Optional max1 = employees.stream().map(Employee::getTotalSalary().max(Double::compare); System.out.println(max1.get()); // 流中元素接收,计算得到一个 List list = Arrays.asList(1, 2, 3, 4, 5); Integer reduce = list.stream().reduce(0, (x, u) -> x + u);//0+1+2.....+10 System.out.println(reduce); Optional reduce1 = employees.stream().map(Employee::getAge).reduce(Integer::sum); System.out.println(reduce1.get() + "----------"); // 收集元素到list employees.stream().map(Employee::getName).distinct().collect(Collectors.toList()).forEach(System.out::println); System.out.println(); // 收集元素到LinkList employees.stream().map(Employee::getName).distinct().collect(Collectors.toCollection(HashSet::new)).forEach(System.out::println); // 获取流中最大值 Optional max2 = employees.stream().map(Employee::getAge).max(Integer::compare); System.out.println(max2.get()); // 收集获取总数(集合总数) Long collect = employees.stream().collect(Collectors.counting()); System.out.println(collect); // 工资的平均值 Double collect1 = employees.stream().collect(Collectors.averagingDouble(Employee::getTotalSalary)); System.out.println(collect1); // 获取工资的总数,综合,最小值,平均值,最大值 DoubleSummaryStatistics collect2 = employees.stream().collect(Collectors.summarizingDouble(Employee::getTotalSalary)); System.out.println(collect2); // 获取年龄最大的员工 Optional collect3 = employees.stream().collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()))); System.out.println(collect3.get()); // 获取年龄最小的员工 Optional collect4 = employees.stream().map(Employee::getTotalSalary).collect(Collectors.minBy(Double::compare)); System.out.println(collect4.get()); // 按薪资分组 Map > collect5 = employees.stream().collect(Collectors.groupingBy(Employee::getTotalSalary)); System.out.println(collect5); // 薪资分区(匹配true) Map > collect6 = employees.stream().collect(Collectors.partitioningBy((e) -> e.getTotalSalary() > 5000d)); System.out.println(collect6); }
总结
到这里Stream介绍基本完成了。以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。