1. Stream 的使用
Stream 是什么?
Stream 是数据渠道,用于操作数据源(数组、集合等)所生成的元素序列。
Java8两大最为重要的改变就是 Lambda表达式 与 Stream API,这两种改变的引入带来的是新的抽象方式 (函数式编程),面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象
Stream 是 Java8 中处理集合的关键抽象概念,可以指定对集合进行的操作,可以执行复杂的查找、过滤、映射数据等操作,Stream API 提供了一种高效且易于使用的处理数据的方式。
四个必须要知道的内置函数接口
- 消费型接口:
Consumer<T> void accept(T t)
有参数,无返回值的抽象方法;- 供给型接口:
Supplier <T> T get()
无参有返回值的抽象方法;- 断定型接口:
Predicate<T> boolean test(T t)
有参,但是返回值类型是固定的boolean;- 函数型接口:
Function<T,R> R apply(T t)
有参有返回值的抽象方法;
// 消费接口定义
Consumer<Student> greeter = (p) -> System.out.println("Hello, " + p.firstName);
// 消费
greeter.accept(new Student("Luke", "Skywalker"));
复制代码
// 供给接口定义
Supplier<Student> supplier = Student::new;
// 供给
supplier.get();
复制代码
// 断定型接口定义
Predicate<String> predicate = (s) -> s.length() > 0;
// 断定
predicate.test("s"); // true
predicate.test(""); // false
复制代码
// 函数型接口定义
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
// 应用
backToString.apply("123"); // "123"
复制代码
Stream 的使用分为三个步骤
- 创建Stream (使用数据源可以是集合、数组来获取流)
- 中间操作 (对数据源的数据进行处理)
- 终止操作 (先执行中间操作产生结果后终止流,之后不能再使用该流)
惰性求值
中间操作不会执行任何的处理,而是在终止操作时一次性全部处理,这就是惰性求值
// 像这样的代码并未做什么实际工作
lists.stream().filter(x -> x != 1)
复制代码
// 像这种有终止操作的代码才会产生新值
List<Integer> list1 = list.parallelStream().filter(x -> x != 1).collect(Collectors.toList());
复制代码
1.1 创建 Stream 的方式
集合创建
- 顺序流:使用顺序方式遍历,每个item读完之后再读下一个item
- 并行流:使用并行遍历,将数据分为多个段,各个段的数据都在不同线程下处理
在多核计算机的情况下理论上并行流会比顺序流快上一倍左右
① 顺序执行:default Stream<E> stream()
返回一个顺序流
// 将list列表通过stream()生一个流过滤1最后打印
List<Integer> list1 = list.stream().filter(x -> x != 1).collect(Collectors.toList());
复制代码
② 并行执行:default Stream<E> parallelStream()
返回一个并行流
List<Integer> list1 = list.parallelStream().filter(x -> x != 1).collect(Collectors.toList());
复制代码
数组创建
通过Arrays的静态方法获取数组流:static <T> Stream<T> stream(T[] array)
// 将array数组通过Arrays的静态方法生一个流过滤1最后打印
Arrays.stream(array).filter(x -> x != 1).forEach(System.out::println);
复制代码
Stream创建
通过调用Stream类的静态方法创建流 (注意后两个生成的是无限流)
① 通过显示值:public static<T> Stream<T> of(T... values)
// 通过显示值1,2,3,4生成一个流过滤掉1最后打印
Stream.of(1,2,3,4).filter(x -> x != 1).forEach(System.out::println);
复制代码
② 通过迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
// 从0开始迭代每次加1生成一个无限流通过limit限制前十位最后打印
Stream.iterate(0, x -> x + 1).limit(10).forEach(System.out::println);
复制代码
③ 通过生成:public static<T> Stream<T> generate(Supplier<T> s)
// 通过Math的随机数函数生成一个无限流通过limit限制前五位最后打印
Stream.generate(Math::random).limit(5).forEach(System.out::println);
复制代码
1.2 中间操作
Stream<T> filter(Predicate<? super T> predicate);
接收Lambda,从流中排除某些元素;
List<Integer> list = Arrays.asList(1,2,3,4);
// filter中传入一个判定型接口Predicate过滤掉遍历这个集合是返回false的结果
List<Integer> list1 = list.stream().filter(x -> x != 1).collect(Collectors.toList());
list1.forEach(System.out::print); // 234
复制代码
Stream<T> distinct();
筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素;
List<Integer> nums = Arrays.asList(1,1,2,3,4);
// 去重
nums.stream().distinct().forEach(System.out::print); // 1234
复制代码
Stream<T> limit(long maxSize);
截断流,使其元素不超过给定数量
// 通过Math的随机数函数生成一个无限流通过limit限制前五位最后打印
Stream.generate(Math::random).limit(5).forEach(System.out::println);
复制代码
Stream<T> skip(long n);
跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
List<Integer> nums = Arrays.asList(1,1,2,3,4);
// 去重
nums.stream().skip(2).forEach(System.out::print); // 234
复制代码
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<Integer> nums = Arrays.asList(1,1,2,3,4);
// 通过map对每个元素进行类型转换
List<Long> collect = nums.stream().map(Long::valueOf).collect(Collectors.toList());
复制代码
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
// 合并多个流
List<Integer> result= Stream.of(Arrays.asList(1,3),Arrays.asList(5,6)).flatMap(Collection::stream).collect(Collectors.toList());
复制代码
Stream<T> sorted();
产生一个新流,其中按自然顺序排序
List<Integer> nums = Arrays.asList(8,6,7,3);
// 自然排序
nums.stream().sorted().forEach(System.out::print); // 3678
复制代码
Stream<T> sorted(Comparator<? super T> comparator);
产生一个新流,其中按比较器顺序排序
List<Integer> nums = Arrays.asList(8,6,7,3);
// 比较器排序
nums.stream().sorted((x,y) -> (y-x)).forEach(System.out::print); // 8763
复制代码
1.3 终止操作
boolean allMatch(Predicate<? super T> predicate);
检查是否匹配所有元素
List<Integer> nums = Arrays.asList(1,1,7,3);
// 检查是否集合里面的元素都等于1
System.out.println(nums.stream().allMatch(x -> x == 1)); // false
复制代码
boolean anyMatch(Predicate<? super T> predicate);
检查是否至少匹配一个元素
List<Integer> nums = Arrays.asList(1,1,7,3);
// 检查是否集合里面的元素有等于1的
System.out.println(nums.stream().anyMatch(x -> x == 1)); // true
复制代码
boolean noneMatch(Predicate<? super T> predicate);
检查是否没有匹配所有元素
List<Integer> nums = Arrays.asList(1,1,7,3);
// 检查是否集合里面的元素都不等于1
System.out.println(nums.stream().noneMatch(x -> x == 1)); // false
复制代码
Optional<T> findFirst();
返回第一个元素
List<Integer> nums = Arrays.asList(1,1,7,3);
// 拿到集合第一个元素用Optional存储可以通过get取出来
System.out.println(nums.stream().findFirst()); // Optional[1]
System.out.println(nums.stream().findFirst()); // 1
复制代码
Optional<T> findAny();
返回当前流中的任意元素
List<Integer> nums = Arrays.asList(1,1,7,3);
// 拿到集合任意元素用Optional存储可以通过get取出来
System.out.println(nums.stream().findFirst()); // Optional[1]
System.out.println(nums.stream().findFirst()); // 1
复制代码
long count();
返回流中元素总数
List<Integer> nums = Arrays.asList(3,7,3);
// 集合大小
System.out.println(nums.stream().count()); // 3
复制代码
Optional<T> max(Comparator<? super T> comparator);
返回流中最大值
List<Integer> nums = Arrays.asList(3,7,3);
// 返回比较器下的最大值用Optional存储通过get取出(注意这里的比较器是从大到小排序)
System.out.println(nums.stream().max((x, y) -> (y - x)).get()); // 3
复制代码
Optional<T> min(Comparator<? super T> comparator);
返回流中最小值
List<Integer> nums = Arrays.asList(3,7,3);
// 返回比较器下的最小值用Optional存储通过get取出(注意这里的比较器是从大到小排序)
System.out.println(nums.stream().min((x, y) -> (y - x)).get()); // 7
复制代码
void forEach(Consumer<? super T> action);
内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代)
List<Integer> nums = Arrays.asList(3,7,3);
// 内部迭代
nums.stream().forEach(System.out::print); // 373
复制代码
Optional<T> reduce(BinaryOperator<T> accumulator);
可以将流中元素反复结合起来,得到一个值。返回 T
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
// 将集合的值变成double类型并抹个零通过reduce反复结合即相加得到总和
double bill = costBeforeTax.stream().map((cost) -> 0.1 * cost).reduce(Double::sum).get();
System.out.println(bill); // 150.0
复制代码
T reduce(T identity, BinaryOperator<T> accumulator);
可以将流中元素反复结合起来,得到一个值。返回
Optional<T>
double bill = 100.0;
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
// 跟上面的比就是多一个初始值在反复结合
bill = costBeforeTax.stream().map((cost) -> 0.1 * cost).reduce(bill, Double::sum);
System.out.println(bill); // 250.0
复制代码
<R, A> R collect(Collector<? super T, A, R> collector);
将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法