Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数 据库查询。
也可以使用Stream API来并行执行操作。
简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列,以下三点注意。
Stream流操作的三大步
1.集合创建Stream流
/**** * java8中的Collection接口方法 * stream() 顺序流 * parallelStream() 并行流(后面再说这个) */ @Test public void test01() { //声明一个list List<String> list = new ArrayList<String>(); // default Stream<E> stream() 顺序流 // default Stream<E> parallelStream() 并行流 // 创建一个顺序流 Stream stream1 = list.stream(); // 创建一个并行流 Stream stream2 = list.parallelStream(); }
2.数组创建Stream流
/**** * 数组创建Stream流 * Java8 中的 Arrays 的静态方法 stream() 可以获取数组流 */ @Test public void test02() { // Java8 中的 Arrays 的静态方法 stream() 可以获取数组流 // public static <T> Stream<T> stream(T[] array) String[] arr = new String[10]; Stream stream = Arrays.stream(arr); }
3.由值创建Stream流 Stream.of()
/**** * 由值创建Stream流 * Stream.of() */ @Test public void test03() { // 可以使用静态方法 Stream.of(), 通过显式值创建一个流。它可以接收任意数量的参数。 Stream stream = Stream.of("1", "2", "3", "4"); }
4.由函数创建Stream无限流 iterate.generate() Stream.generate()
/**** * 由函数创建Stream无限流 * * seed种子 f 一元运算接口 * Stream.iterate(T seed,UnaryOperator f) * * supplier 供给型型接口 * Stream.generate(Supplier supplier) */ @Test public void test04() { // 可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。 // args1 seed 开始种子, args2 一元函数式接口 //迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) Stream stream1 = Stream.iterate(0, (x) -> x + 1); //args 一个供给型接口 // 生成 public static<T> Stream<T> generate(Supplier<T> s) : Stream stream2 = Stream.generate(() -> new Random().nextInt(100)); }
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为“惰性求值”。看一下一些常用的api:
1.流中排除不符合条件的元素:filter
/**** * filter(Predicate p) * 接收一个断言式接口,从流中排除不符合条件的元素 */ @Test public void test01() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //使用Stream流的静态方法 of创建一个Stream流 Stream<Integer> stream = Stream.of(nums); //过滤操作 接收一个断言式接口 排除不符合条件的元素 输出结果 stream.filter((x) -> x > 5).forEach(System.out::println); }
2.流中去除重复元素:distinct
/*** * distinct() * 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 */ @Test public void test02() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; //使用Stream流的静态方法 of创建一个Stream流 Stream<Integer> stream = Stream.of(nums); //去除重复的元素 stream.distinct().forEach(System.out::println); }
3.截取流获取前n个元素:limit
/*** * limit(long maxSize) * 接收一个long型数值流中的元素个数不操过maxSize个, */ @Test public void test03() { //使用Stream流的静态方法 generate创建一个Stream无限流 Stream stream = Stream.generate(() -> new Random().nextInt(100)); //截断流获取前n个元素 最大n个 stream.limit(10).forEach(System.out::println); }
4.跳过流前n个元素:skip
/*** * skip(long n) * 接收一个long型数值 跳过流前n个元素获取后面的元素和 limit(n)相反 */ @Test public void test04() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; //使用Stream流的静态方法 generate创建一个Stream无限流 Stream stream = Arrays.stream(nums); stream.skip(3).forEach(System.out::println); }
5.映射流中的每一个元素:map
/**** * map(Function f) * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 */ @Test public void test05() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //使用Stream流的静态方法 generate创建一个Stream无限流 Stream<Integer> stream = Arrays.stream(nums); // 将每个元素*2 stream.map(x -> x * 2).forEach(System.out::println); }
6.将流中元素映射成一个新的Double型元素:mapToDouble
/**** * mapToDouble(ToDoubleFunction f) * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的Double型元素。 * 注意原数组要可以转换成Double型 */ @Test public void test06() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //使用Stream流的静态方法 generate创建一个Stream无限流 Stream<Integer> stream = Arrays.stream(nums); // 将每个元素 变成double stream.mapToDouble(x -> x * 2).forEach(System.out::println); }
7.将流中元素映射成一个新的Long型元素:mapToDouble
/**** * mapToLong(ToDoubleFunction f) * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的Long型元素。 * 注意原数组要可以转换成Long型 */ @Test public void test07() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //使用Stream流的静态方法 generate创建一个Stream无限流 Stream<Integer> stream = Arrays.stream(nums); // 将每个元素 变成double stream.mapToDouble(x -> x * 2).forEach(System.out::println); }
8.将流中的每个值都换成另一个流,然后把所有流连接成一个流:flatMap
/**** * flatMap(ToDoubleFunction f) * 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 */ @Test public void test08() { String[] strings = {"hello lambda", "hello", "lambda"}; // Stream的静态方法 of获取流 Stream<String> testStream = Stream.of(strings); // 流中的元素换成另一个流 分割处理 然后去重 打印 testStream.flatMap(str -> Arrays.stream(str.split(" "))).distinct().forEach(System.out::println); }
9.产生一个新流,其中按自然顺序排序:sorted
/** * sorted() * 产生一个新流,其中按自然顺序排序 */ @Test public void test09() { Integer[] nums = {1, 4, 2, 6, 3, 0, 9, 8, 7, 5}; Arrays.stream(nums).sorted().forEach(System.out::println); }
10.产生一个新流,其中按比较器顺序排序:sorted
/** * sorted(Comparator comparator) * 产生一个新流,其中按比较器顺序排序 */ @Test public void test10() { Integer[] nums = {-1, 4, -2, 6, -3, 0, 9, -8, 7, -5}; //例如 按照绝对值排序 Arrays.stream(nums).sorted(Comparator.comparing(x -> Math.abs(x))).forEach(System.out::println); }
终端操作会从流的流水线生成结果。 其结果可以是任何不是流的值,例如:List、Integer、boolean
1.判断流中的元素是否全部满足某一个条件:allMatch
/**** * allMatch(Predicate predicate) * 接收一个断言式接口 全匹配 返回boolean */ @Test public void test01() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //使用Stream流的静态方法 of创建一个Stream流 Stream<Integer> stream = Stream.of(nums); // 匹配数组元素是否全部大于等于0 System.out.println(stream.allMatch((x) -> x >= 0)); }
2.判断流中的元素至少满足某一个条件:anyMatch
/**** * anyMatch(Predicate predicate) * 接收一个断言式接口 至少匹配一个 返回boolean */ @Test public void test02() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //使用Stream流的静态方法 of创建一个Stream流 Stream<Integer> stream = Stream.of(nums); // 匹配数组元素是否有大于10的元素 System.out.println(stream.anyMatch((x) -> x > 10)); }
3.判断流中的元素都不满足某一个条件:noneMatch
/**** * noneMatch(Predicate predicate) * 接收一个断言式接口 是否没有匹配的元素 返回boolean */ @Test public void test03() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; //使用Stream流的静态方法 of创建一个Stream流 Stream<Integer> stream = Stream.of(nums); // 匹配数组元素是否没有大于10的元素 System.out.println(stream.noneMatch((x) -> x > 10)); }
4.返回流元素中的第一个元素:findFirst
/**** * findFirst() * 返回Optional */ @Test public void test04() { Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; //使用数组Arrays创建流 Stream stream = Arrays.stream(nums); //返回流元素中的第一个元素 Optional optional = stream.findFirst(); System.out.println(optional.get()); }
5.返回流元素中的任意一个元素:findAny
/**** * findAny() * 返回Optional */ @Test public void test05() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; //使用数组Arrays创建流 Stream stream = Arrays.stream(nums); //返回流元素中的任意元素 Optional optional = stream.findAny(); System.out.println(optional.get()); }
6.返回流元素总数:count
/**** * count() * 返回流元素总数 Long型 */ @Test public void test06() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; //使用数组Arrays创建流 Stream stream = Arrays.stream(nums); Long aLong = stream.count(); System.out.println(aLong); }
7.返回流元素中的最大元素:max
/**** * max(Comparator comparator) * 返回流元素最大值 * 接收一个比较器 */ @Test public void test07() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; //使用数组Arrays创建流 Stream<Integer> stream = Arrays.stream(nums); //返回流元素中的最大元素 Optional optional = stream.max(Integer::compare); System.out.println(optional.get()); }
8.返回流元素中的最小元素:min
/**** * max(Comparator comparator) * 返回流元素最小值 * 接收一个比较器 */ @Test public void test08() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; //使用数组Arrays创建流 Stream<Integer> stream = Arrays.stream(nums); //返回流元素中的最小元素 Optional optional = stream.min(Integer::compare); System.out.println(optional.get()); }
9.内部迭代:forEach
/**** * forEach(Consumer consumer) * 接收一个消费性接口 * 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。 * 相反,Stream API 使用内部 迭代——它帮你把迭代做了) */ @Test public void test09() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; Arrays.stream(nums).forEach(System.out::println); }
10.将流中元素反复结合起来,得到一个值:reduce
/*** * reduce(BinaryOperator<T> accumulator) * 可以将流中元素反复结合起来,得到一个值。返回Optional * 接收一个二元运算接口 */ @Test public void test10() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; // 依次累加 Object object = Arrays.stream(nums).reduce((x, y) -> x + y).get(); System.out.println(object); }
11.将流中元素反复结合起来(有一个起始值),得到一个值:reduce
/*** * reduce(T iden, BinaryOperator<T> accumulator) * 接收一个二元运算接口 * 可以将流中元素反复结合起来,得到一个值。返回 T */ @Test public void test11() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; // iden 作为一个值先加入运算然后 依次累加 Object object = Arrays.stream(nums).reduce(10, (x, y) -> x * y); System.out.println(object); }
12.将流转换为List:stream.collect(Collectors.toList())
/*** * collect(Collector collector) * 将流转换为其他形式。 * 接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 * Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例 * Collectors.toList() * // 将数组变成了 list */ @Test public void test12() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; Stream<Integer> stream = Arrays.stream(nums); List list = stream.collect(Collectors.toList()); // 将数组变成了 list System.out.println(list.size()); }
13.将流转换为Set:stream.collect(Collectors.toSet())
/*** * Collectors.toSet() * 将数组变成了 set */ @Test public void test13() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; Stream<Integer> stream = Arrays.stream(nums); Set set = stream.collect(Collectors.toSet()); // 将数组变成了 set System.out.println(set.size()); }
14.将流转换为Collection:stream.collect(Collectors.toSet())
/*** * Collectors.toCollection(ArrayList::new) * Collectors.toCollection(HashSet::new) * 将数组变成了 Collection */ @Test public void test14() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; Stream<Integer> stream = Arrays.stream(nums); Collection collection = stream.collect(Collectors.toCollection(HashSet::new)); // 将数组变成了 collection System.out.println(collection.size()); }
15.计算流中元素的个数:stream.collect(Collectors.counting())
/*** * Collectors.counting() * 计算流中元素的个数 */ @Test public void test15() { Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1}; Stream<Integer> stream = Arrays.stream(nums); Long integer = stream.collect(Collectors.counting()); System.out.println(integer); }
16.计算流中元素的对象的某一个属性平均值:stream.collect(Collectors.summingDouble())
/*** * Collectors.summingDouble(ToDoubleFunction mapper) * 计算流中元素的对象的某一个属性平均值 * 接收一个 计算int、long、 double、值的函数 接口 */ @Test public void test16() { List<Employee> employees = new ArrayList<>(3); Employee employe1 = new Employee(1, "A1", 5000); Employee employe2 = new Employee(1, "A2", 8000); Employee employe3 = new Employee(1, "A3", 10000); employees.add(employe1); employees.add(employe2); employees.add(employe3); Stream<Employee> stream = employees.stream(); Double sumSalary = stream.collect(Collectors.summingDouble(Employee::getSalary)); System.out.println(sumSalary); }
17.计算流中元素的对象的某一个属性平均值:stream.collect(Collectors.averagingDouble())
/*** * Collectors.averagingDouble(ToDoubleFunction mapper) * 计算流中元素的对象的某一个属性平均值 * 接收一个 计算int、long、 double、值的函数 接口 */ @Test public void test17() { List<Employee> employees = new ArrayList<>(3); Employee employe1 = new Employee(1, "A1", 5000); Employee employe2 = new Employee(1, "A2", 8000); Employee employe3 = new Employee(1, "A3", 10000); employees.add(employe1); employees.add(employe2); employees.add(employe3); Stream<Employee> stream = employees.stream(); Double avgSalary = stream.collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avgSalary); }
18.连接流中元素的对象的某一个属性字符串:stream.collect(Collectors.joining())
/*** * Collectors.joining() * 连接流中元素的对象的某一个属性字符串 */ @Test public void test18() { List<Employee> employees = new ArrayList<>(3); Employee employe1 = new Employee(1, "A1", 5000); Employee employe2 = new Employee(1, "A2", 8000); Employee employe3 = new Employee(1, "A3", 10000); employees.add(employe1); employees.add(employe2); employees.add(employe3); Stream<Employee> stream = employees.stream(); String names = stream.map((x) -> x.getName() + "-").collect(Collectors.joining()); System.out.println(names); }
19.收集器中最小值:stream.collect(Collectors.minBy())
/*** * Collectors.maxBy(Comparator comparator) * 根据比较器选择最小值 * 接收一个比较器 */ @Test public void test19() { Integer[] nums = {1, 2, 3, 4, 5, 7, 8, 9, 3, 4, 56}; Stream<Integer> stream = Arrays.stream(nums); Optional optional = stream.collect(Collectors.minBy(Integer::compareTo)); System.out.println(optional.get()); }
20.收集器中某一属性之和:stream.collect(Collectors.reducing())
/*** * Collectors.reducing(U identity,Function<? super T, ? extends U> mapper,BinaryOperator<U> op) * 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 * 接收二元计算接口 */ @Test public void test20() { List<Employee> employees = new ArrayList<>(3); Employee employe1 = new Employee(1, "A1", 5000); Employee employe2 = new Employee(1, "A2", 8000); Employee employe3 = new Employee(1, "A3", 10000); employees.add(employe1); employees.add(employe2); employees.add(employe3); Stream<Employee> stream = employees.stream(); // identity : 0d 类型和后面的元素类型 保持一致 Double aDouble = stream.collect(Collectors.reducing(0d, Employee::getSalary, Double::sum)); System.out.println(aDouble); }
21.收集器转换为另一类型然后处理:stream.collect(Collectors.collectingAndThen())
/*** * Collectors.collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher) * 转换函数返回的类型 * 包裹另一个收集器,对其结果转换函数 */ @Test public void test21() { List<Employee> employees = new ArrayList<>(3); Employee employe1 = new Employee(1, "A1", 5000); Employee employe2 = new Employee(1, "A2", 8000); Employee employe3 = new Employee(1, "A3", 10000); employees.add(employe1); employees.add(employe2); employees.add(employe3); Stream<Employee> stream = employees.stream(); // 转换函数返回的类型 返回set // 包裹另一个收集器,对其结果 set 进行处理 判断时候为空 Boolean bool = stream.collect(Collectors.collectingAndThen(Collectors.toSet(), Set::isEmpty)); System.out.println(bool); }
22.收集器按照某一属性分组:stream.collect(Collectors.groupingBy())
/*** * 根据某属性值对流分组,属 性为K,结果为V * Collectors.groupingBy(Function<? super T, ? extends K> classifier) * 传入一个 代表流元素的属性 * 返回 以属性为key value为 list的map */ @Test public void test22() { List<Employee> employees = new ArrayList<>(3); Employee employe1 = new Employee(1, "A1", 5000); Employee employe2 = new Employee(1, "A2", 8000); Employee employe3 = new Employee(1, "A3", 10000); employees.add(employe1); employees.add(employe2); employees.add(employe3); Stream<Employee> stream = employees.stream(); Map map = stream.collect(Collectors.groupingBy(Employee::getName)); Set set=map.entrySet(); for (Object str:set) { System.out.println(str); System.out.println(map.get(str)); } }
23.收集器按照某一属性(boolean类型)分组:stream.collect(Collectors.partitioningBy())
/*** * 根据true或false进行分区 * Collectors.groupingBy(Function<? super T, ? extends K> classifier) * 传入一个 代表流元素的属性 * 返回 以属性(flase/true)为key value为 list的map */ @Test public void test23() { List<Employee> employees = new ArrayList<>(3); Employee employe1 = new Employee(1, "A1", 5000,false); Employee employe2 = new Employee(1, "A2", 8000,true); Employee employe3 = new Employee(1, "A3", 10000,false); employees.add(employe1); employees.add(employe2); employees.add(employe3); Stream<Employee> stream = employees.stream(); Map map = stream.collect(Collectors.partitioningBy(Employee::getFlag)); Set set=map.entrySet(); for (Object str:set) { System.out.println(str); System.out.println(map.get(str)); } }
辅助类Employee
/** * @author black猫 * @date 2019-11-25 * 员工类 */ public class Employee { private int id; private String name; private double salary; private boolean flag; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public boolean getFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public Employee(int id, String name, double salary){ this.id=id; this.name=name; this.salary=salary; } public Employee(int id, String name, double salary,boolean flag){ this.id=id; this.name=name; this.salary=salary; this.flag=flag; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '/'' + ", salary=" + salary + ", flag=" + flag + '}'; } }
Stream是java8提供的java.util.stream.*包下的强大Api,结合lambda表达式,可以快速、高效、便捷的对集合数据进行处理, 熟练掌握之后,就像写sql一样操作集合数据,功能强大,代码不再那么臃肿、冗余。爽!!!
文章首发于 黑猫のBlog 欢迎来留言啊!!!