💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 简介 在JDK1.8开始发现类集里面提供的接口都出现大量的default或者是static方法 以Collection的父接口Iterable接口里面定义的一个方法来观察: ~~~ default void forEach(Consumer<? super T> action) ~~~ 例子: 利用forEach来输出 ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "A", "B", "c", "D", "E"); all.forEach(System.out::println); ~~~ 不会采用以上方式完成,因为forEach()只能输出,但是我们很多时候,在输出的时候还要对数据进行处理,也就是Iterator输出是我们主要形式 除了使用Iterator迭代输出之外,JDK1.8还提供了一个专门可以进行数据处理的类就是stream类 ~~~ java.util.stream ~~~ 这个类的对象可以利用Collection接口提供的方法操作: ~~~ default Stream<E> stream(); ~~~ # 注意 1. Stream 自己不会存储元素 2. Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream 3. Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行 Stream的使用流程: * 步骤一:Stream的实例化 * 步骤二:一系列的中间操作 * 步骤三:终止操作 注意: 1. 步骤二中的中间操作可以有多个 2. 如果没有终止操作,那么一系列的中间操作是不会执行的。只有执行了步骤三的终止操作,步骤二才会执行:惰性求值 3. 终止操作一旦执行,就不可以再执行中间操作或其他的终止操作。 # 实例化 ## 通过集合 ~~~ //返回一个顺序流 default stream<E> stream(); //返回一个并行流 default stream<E> parallelStream(); ~~~ **返回一个顺序流** ~~~ ArrayList<String> strings = new ArrayList<>(); Collections.addAll(strings, "A", "B", "c", "D", "E"); Stream<String> stream = strings.stream(); ~~~ **返回一个并行流** ~~~ ArrayList<String> strings = new ArrayList<>(); Collections.addAll(strings, "A", "B", "c", "D", "E"); Stream<String> stream = strings.parallelStream(); ~~~ ## 通过数组 java8中Arrays的静态方法stream()可以获取数组流 * `static <T> Stream<T> stream(T[] array)` 返回一个流 重载形式,能够处理对应基本类型的数组 * public static IntStream stream(int[] array) * public static LongStream stream(long[] array) * public static DoubleStream stream(double[] array); ~~~ String[] arr = {"MM", "GG", "JJ", "DD"}; Stream<String> stream = Arrays.stream(arr); ~~~ ## 通过Stream的of() 可以调用Stream类静态方法of(),通过显示值创建一个流.它可以接收任意数量的参数 * `public static<T> Stream<T> of(T... values)` 返回一个流 ~~~ Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); ~~~ ## 创建无限流 可以使用静态方法Stream.iterate()和Steam.generate()创建无限流 * 迭代 `public static<T> Stream<T> iterate(final T seed, final UnartyOperator<T> f)` * 生成 `public static<T> Stream<T> generate(Supplier<T> s)` 迭代 ~~~ Stream<Integer> stream = Stream.iterate(0, x -> x + 2); stream.forEach(System.out::println); ~~~ 生成 ~~~ Stream<Double> stream = Stream.generate(Math::random); stream.forEach(System.out::println); ~~~ # 中间操作 多个**中间操作**可以连接起来形成一个**流水线**,除非流水线上触发了终止操作,否则**中间操作不会执行任何的处理.** **而在终止操作时一次性全部处理,称为"惰性求值"** ## 筛选与切片 | 方法 | 描述 | | --- | --- | | filter(Predicate p) | 接收Lambda,从流中排除某些元素 | | distinct() | 筛选,通过流所生产元素的hashCode()和equals()去重 | | limit(long maxSize) | 截断流,使其元素不超过给定数量 | | skip(long n) | 跳过元素,返回一个扔掉了前n个元素的流.若流中的元素不足n个,则返回一个空流.与limit(n)互补 | ## 映射 | 方法 | 描述 | | --- | --- | | map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 | | mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream | | mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream | | mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream | | flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 | ## 排序 | 方法 | 描述 | | --- | --- | | sorted() | 产生一个新流,其中按自然顺序排序 | | sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 | 进行排序:失败。 原因:没有实现Comparable接口 ~~~ list1.stream().sorted((e1,e2) -> { if(e1.getAge() != e2.getAge()){ return e1.getAge() - e2.getAge(); }else{ //double比较 return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); ~~~ # 终止操作 终端操作会从流的流水线生成结果. 其结果可以是任何不适流的值,例如:List,Integer,甚至是void. 流进行终止操作后,不能再次使用 ## 匹配查找 | 方法 | 描述 | | --- | --- | | allMatch(Predicate p) | 检查是否匹配所有元素 | | anyMatch(Predicate p) | 检查是否至少匹配一个元素 | | noneMatch(Predicate p) | 检查是否没有匹配所有元素 | | findFirst() | 返回第一个元素 | | findAny() | 返回当前流中的任意元素 | | count() | 返回流中元素的总个数 | | max(Comparator c) | 返回流中最大值 | | min(Comparator c) | 返回流中最小值 | | forEach(Consumer c) | 内部迭代 | ~~~ List<Employee> list = EmployeeData.getEmployees(); // allMatch(Predicate p)——检查是否匹配所有元素 //是否所有的员工的年龄都大于18 boolean b = list.stream().allMatch(e -> e.getAge() > 18); System.out.println(b); // anyMatch(Predicate p)——检查是否至少匹配一个元素 //是否存在员工的工资大于 10000 boolean b1 = list.stream().anyMatch(e -> e.getSalary() > 9900); System.out.println(b1); // noneMatch(Predicate p)——检查是否没有匹配的元素 //是否存在员工姓“雷” boolean b2 = list.stream().noneMatch(e -> e.getName().contains("雷")); System.out.println(b2); // count——返回流中元素的总个数 long count = list.stream().filter(e -> e.getSalary() > 5000).count(); System.out.println(count); // 练习:返回最高的工资: Stream<Employee> stream = list.stream(); Stream<Double> stream1 = stream.map(Employee::getSalary); Optional<Double> max = stream1.max(Double::compare); System.out.println(max.get()); // min(Comparator c)——返回流中最小值 // 练习:返回最低工资的员工 Stream<Employee> stream2 = list.stream(); Optional<Employee> min = stream2.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(min.get()); ~~~ ## 归约 | 方法 | 描述 | | --- | --- | | reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值,返回T | | reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值,返回Optional<T> | ~~~ List<Integer> list = Arrays.asList(1,2,3,4,5,6); // Integer sum = list.stream().reduce(0, (x1, x2) -> x1 + x2); //如果是空就会取默认值10 Integer sum = list.stream().reduce(10, Integer::sum); System.out.println(sum); // reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T> // 练习1:计算公司所有员工工资的总和 List<Employee> emps = EmployeeData.getEmployees(); Stream<Double> moneyStream = emps.stream().map(Employee::getSalary); Optional<Double> moneyOptional = moneyStream.reduce(Double::sum); System.out.println(moneyOptional.get()); ~~~ ## 收集 | 方法 | 描述 | | --- | --- | | collect(Collector c) | 将流转换为其他形式.接收一个Collector接口的实现,用于给stream中元素做汇总的方法 | Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List,Set,Map) 另外,Collectors使用类提供了很多静态方法,可以方便地创建常见收集器实例 ![](https://box.kancloud.cn/3241c2b96b8e7203007f0c5d78b31c1b_967x617.png) # 例子 ## 取得Stream对象并统计个数 ~~~ long count(); ~~~ ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "A", "B", "c", "D", "E"); Stream<String> stream = all.stream(); //取得个数 System.out.println(stream.count()); ~~~ ## 去掉重复数据 ~~~ Stream<T> distinct(); ~~~ ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "A", "B", "E", "A", "a"); Stream<String> stream = all.stream(); //去重后的数据 System.out.println(stream.distinct().count()); ~~~ ## 收集器(最后使用) ~~~ <R, A> R collect(Collector<? super T, A, R> collector); ~~~ 需要Collectors方法 ~~~ public static <T> Collector<T, ?, List<T>> toList() ~~~ ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "A", "B", "E", "A", "a"); Stream<String> stream = all.stream(); //去除掉所有的重复数据后形成的新的集合,里面是不包含重复内容的集合 List<String> collect = stream.distinct().collect(Collectors.toList()); System.out.println(collect); ~~~ 既然Stream类是进行数据处理的,那么在数据处理过程中就不可能不进行数据筛选(过滤) ## 数据过滤 ~~~ Stream<T> filter(Predicate<? super T> predicate); ~~~ ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "A", "B", "E", "A", "a"); Stream<String> stream = all.stream(); //增加了数据的过滤操作,使用了断言行的函数接口,使用了string的contains List<String> collect = stream.distinct().filter((x) -> x.contains("a")).collect(Collectors.toList()); System.out.println(collect); ~~~ 数据过滤是区分大小写的,那么在数据过滤前要对数据进行处理呢? ## map数据处理方法 map是针对数据逐行处理 ~~~ <R> Stream<R> map(Function<? super T, ? extends R> mapper); ~~~ ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "Android", "Java", "Ios", "jsp", "ORACLE"); Stream<String> stream = all.stream(); List<String> collect = stream.distinct() .map((x) -> x.toLowerCase()) .filter((x) -> x.contains("a")) .collect(Collectors.toList()); System.out.println(collect); ~~~ ## 集合数据分页 在Stream接口里面提供有进行集合数据分页的操作 * 设置跳过的数据行数 ~~~ public Stream<T> skip(long n); ~~~ * 设置取出的数据个数 ~~~ public Stream<T> limit(long maxSize); ~~~ ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "Android", "Java", "Ios", "jsp", "ORACLE"); Stream<String> stream = all.stream(); List<String> collect = stream.distinct() .map((x) -> x.toLowerCase()) .skip(2).limit(2) .collect(Collectors.toList()); System.out.println(collect); ~~~ 在stream可以进行数据全匹配和部分比配 ## 数据匹配 * 全匹配 ~~~ public boolean allMatch(Predicate<? super T> predicate); ~~~ * 匹配任意一个 ~~~ public boolean anyMatch(Predicate<? super T> predicate); ~~~ ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "Android", "Java", "Ios", "jsp", "ORACLE"); Stream<String> stream = all.stream(); boolean bool = stream.anyMatch((x) -> x.contains("jsp")); System.out.println(bool); ~~~ 在实际之中有可能会出现多个匹配条件,在断言型接口函数式接口里面提供有如下的方法 * 或操作 ~~~ default Predicate<T> or(Predicate<? super T> other); ~~~ * 与操作 ~~~ default Predicate<T> and(Predicate<? super T> other); ~~~ **设置多个条件** ~~~ ArrayList<String> all = new ArrayList<>(); boolean b = Collections.addAll(all, "Android", "Java", "Ios", "jsp", "ORACLE", "jspIos"); Stream<String> stream = all.stream(); Predicate<String> p1 = (x) -> x.contains("jsp"); Predicate<String> p2 = (x) -> x.contains("Ios"); //同时使用2个条件 if (stream.anyMatch(p1.or(p2))) { System.out.println("数据存在"); } ~~~ 利用这样的匹配条件,可以针对数据进行方便的查询操作 如果要想更好的返回stream的操作优势,必须结合MapReduce ## 数据分析reduce 就是做数据统计使用的 ~~~ public Optional<T> reduce(BinaryOperator<T> accumulator); ~~~ 我们点进去看下 ~~~ public interface BinaryOperator<T> extends BiFunction<T,T,T> public interface BiFunction<T, U, R> | |--- R apply(T t, U u); ~~~ 比如一个购物类 ~~~ class ShopCar { //商品名称 private String pname; //商品单价 private double price; //购买数量 private int amount; public ShopCar(String pname, double price, int amount) { this.pname = pname; this.price = price; this.amount = amount; } public String getPname() { return pname; } public double getPrice() { return price; } public int getAmount() { return amount; } } ~~~ 设计的时候设计出了商品的单价与数量,这样如果获取一个商品花费的钱,就要单价乘以数量 例子:求每个商品花费的数量和金额 ~~~ all.add(new ShopCar("java", 800, 20)); all.add(new ShopCar("php", 100, 10)); all.add(new ShopCar("c++", 200, 15)); all.add(new ShopCar("c", 300, 30)); Stream<ShopCar> stream = all.stream(); all.stream().map((x) -> x.getAmount() * x.getPrice()) .forEach(System.out::println); ~~~ 但是这时候处理没有总价,于是数据处理的总价就用reduce完成 ~~~ ArrayList<ShopCar> all = new ArrayList<>(); all.add(new ShopCar("java", 800, 20)); all.add(new ShopCar("php", 100, 10)); all.add(new ShopCar("c++", 200, 15)); all.add(new ShopCar("c", 300, 30)); Stream<ShopCar> stream = all.stream(); Double s = all.stream().map((x) -> x.getAmount() * x.getPrice()) .reduce((sum, m) -> sum + m).get(); System.out.println(s); ~~~ 以上只是实现了一个最简单的MapReuce,但是完成的统计实在有限,如果要更完善统计,需要使用如下方法 ## 统计 ~~~ * 按照Double处理 DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper); * 按照Int处理 IntStream mapToInt(ToIntFunction<? super T> mapper); * 按照Long处理 LongStream mapToLong(ToLongFunction<? super T> mapper); ~~~ ~~~ ArrayList<ShopCar> all = new ArrayList<>(); all.add(new ShopCar("java", 800, 20)); all.add(new ShopCar("php", 100, 10)); all.add(new ShopCar("c++", 200, 15)); all.add(new ShopCar("c", 300, 30)); Stream<ShopCar> stream = all.stream(); DoubleSummaryStatistics dss = all.stream().mapToDouble((x) -> x.getAmount() * x.getPrice()) .summaryStatistics(); System.out.println("商品个数: " + dss.getCount()); System.out.println("商品总花费: " + dss.getSum()); System.out.println("平局花费: " + dss.getAverage()); System.out.println("最高花费: " + dss.getMax()); System.out.println("最低花费: " + dss.getMin()); ~~~