3.4 Foreach Peek(Intermediate) 输出
记录:
Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序
1.2 与传统迭代器的区分
传统迭代器是单向处理,数据按照一个方向流动,当然LisT的Iterator 提供了加强版本:
Stream 可以并行化操作,迭代器只能命令式地、串行化操作
原理实现:Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。Java 的并行 API 演变历程基本如下:
Intermediate(中间操作 不触发操作 Lambda延迟性):一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射 / 过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
Terminal(终止操作 触发整个流的操作):一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用 “光” 了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
还有一种操作被称为 short-circuiting。用以指:
对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。
对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
划重点:
Stream 的每个元素进行转换,而且是执行多次,多个转换操作只会在 Terminal 操作的时候融合起来,一次循环完成. 这样时间复杂度就是 N(N为操作的具体的个数)
// 1. Individual values Stream stream = Stream.of("a", "b", "c"); // 2. Arrays String [] strArray = new String[] {"a", "b", "c"}; stream = Stream.of(strArray); stream = Arrays.stream(strArray); // 3. Collections List<String> list = Arrays.asList(strArray); stream = list.stream();
2.2: 三大包装类型的构造
可以使用 Stream<Integer> Stream<Double> Stream<Long> 但是 Boxing unboxing (装箱 拆箱非常耗时)
IntStream ints = IntStream.of(1,2,3); LongStream longs = LongStream.of(4,5,6); DoubleStream doubles = DoubleStream.of(6,7,8); IntStream.range(1, 10).forEach(System.out::print);// [1,10) 区间 System.out.println(); IntStream.rangeClosed(1, 10).forEach(System.out::print);//[1,10] 区间
2.3 并行流的规则输出
parallel() 方法将普通流转换为并行流
IntStream.range(1, 10).parallel().forEach(System.out::print); // 并行执行 效率高 但是输出结果不具备输入结果的有序性 IntStream.range(1, 10).parallel().forEachOrdered(System.out::print);// 并行执行 效率高 严格要求输出结果按照输入结果预定
2.4 流的转换
collect() 方法
// 1. Array String[] strArray1 = stream.toArray(String[]::new); // 2. Collection List<String> list1 = stream.collect(Collectors.toList()); List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new)); Set set1 = stream.collect(Collectors.toSet()); Stack stack1 = stream.collect(Collectors.toCollection(Stack::new)); // 3. String String str = stream.collect(Collectors.joining()).toString();
注意: 一个Stream只能使用一次,terminal终结最后的操作
Filter Map(FatMap,MapToXXmap) Sorted() limit() skip distinct peek sequential、 unordered
ForEach ForOrderEach Max Min Collect count toArray、 reduce、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
findFisrt findAny AnyMatch AllMatch NoneMatch limit
参数为Function<T,R> 可以理解为转换流
// 一对一 IntStream.of(1,2,3).map(x->x*2).forEach(System.out::println); // 合并流 Stream<List<Integer>> inputStream = Stream.of( Arrays.asList(1), Arrays.asList(2,3), Arrays.asList(4,5,6)); Stream<Integer> child_stream = inputStream.flatMap(x->x.stream()); // 合并流到其他类型 一对多 DoubleStream doubleStream = inputStream.flatMapToDouble(x->x.stream().mapToDouble(Double::new)); IntStream intStream = inputStream.flatMapToInt(x->x.stream().mapToInt(Integer::new)); LongStream longStream = inputStream.flatMapToLong(x->x.stream().mapToLong(Long::new)); // 转大小写 List<String> output = wordList.stream().map(String::toUpperCase) .collect(Collectors.toList()); // 平方数 List<Integer> nums = Arrays.asList(1, 2, 3, 4); List<Integer> squareNums = nums.stream(). map(n -> n * n). collect(Collectors.toList());
参数为Predicate 结果集为返回true的集合
//留下偶数 IntStream.range(1, 10).filter(x->(x&1)==0).forEach(System.out::println); Integer[] sixNums = {1, 2, 3, 4, 5, 6}; Integer[] array = Stream.of(sixNums).filter(x->(x&1)==0).toArray(Integer[]::new); // 把单词挑出来 List<String> output = reader.lines(). flatMap(line -> Stream.of(line.split(REGEXP))). filter(word -> word.length() > 0). collect(Collectors.toList());
终结操作用于输出 ,一个流只能用一次
forEachOrdered 在并行情况为保证一定有序输出. Peek 内部参数 Consumer 执行操作后 返回一个新的Stream
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1,2,3,4,5,6)); stream.parallel().forEach(System.out::println); stream.parallel().forEachOrdered(System.out::println); //并行 强制有序 // 体现了 访问者设计模式 Stream.of("one", "two", "three", "four") .filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList()); // 输出 Filtered value: three Mapped value: THREE Filtered value: four Mapped value: FOUR
3.5 finalFist findAny
返回Optional 非终结操作,可以结果继续处理 使用它的目的是尽可能避免 NullPointerException。 indAny、max/min、reduce 等方法等返回 Optional 值
Integer[] sixNums = {1, 2, 3, 4, 5, 6}; Stream.of(sixNums).findFirst().ifPresent(System.out::println); Stream.of(sixNums).findAny().ifPresent(System.out::println); // 返回的Optional 可以加上逻辑排除NPE Integer else1 = Stream.of(sixNums).filter(x->x<0).findAny().orElse(null); System.out.println(else1); // findFisrt findAny 找不到元素抛出NPE 可以加上Or系列方法 返回默认值
这个方法的主要作用是把 identity 作为第二个参数BinaryOperator 函数的输入,执行操作后返回
T reduce(T identity, BinaryOperator<T> accumulator);
规则:
只有一个参数的时候BinaryOperator 返回Optional
具有两个参数的时候则返回一个具体的运算结果
// 字符串连接,concat = "ABCD" String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); // 求最小值,minValue = -3.0 double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); // 求和,sumValue = 10, 有起始值 int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // 求和,sumValue = 10, 无起始值 sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); // 过滤,字符串连接,concat = "ace" concat = Stream.of("a", "B", "c", "D", "e", "F"). filter(x -> x.compareTo("Z") > 0). reduce("", String::concat);
以上 字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素
public void testLimitAndSkip() { List<Person> persons = new ArrayList(); for (int i = 1; i <= 10000; i++) { Person person = new Person(i, "name" + i); persons.add(person); } List<String> personList2 = persons.stream().map(Person::getName).limit(10).skip(3).collect(Collectors.toList()); System.out.println(personList2); } private class Person { //get set }
注意:
limit/skip ,放在Sorted()后面并不能影响排序的次数
有一种情况是 limit/skip 无法达到 short-circuiting 目的的,就是把它们放在 Stream 的排序操作后,原因跟 sorted 这个 intermediate 操作有关:此时系统并不知道 Stream 排序后的次序如何,所以 sorted 中的操作看上去就像完全没有被 limit 或者 skip 一样
并行流情况下不能使用Limit() 将会影响并行操作的次序性能
对一个 parallel 的 Steam 管道来说,如果其元素是有序的,那么 limit 操作的成本会比较大,因为它的返回对象必须是前 n 个也有一样次序的元素。取而代之的策略是取消元素间的次序,或者不要用 parallel Stream
List<Person> persons = new ArrayList(); for (int i = 1; i <= 5; i++) { Person person = new Person(i, "name" + i); persons.add(person); } List<Person> personList2 = persons.stream().limit(2).sorted((p1, p2) -> p1.getName().compareTo(p2.getName())).collect(Collectors.toList()); System.out.println(personList2);
用 findFirst 来实现,但前者的性能会更好,为 O(n),而 sorted 的成本是 O(n log n)。同时它们作为特殊的 reduce 方法被独立出来也是因为求最大最小值是很常见的操作
Stream<Integer> stream = Stream.generate(()->new Random().nextInt()).limit(100); long nums = 100-stream.distinct().count(); System.out.println(nums); Integer max = stream.max(Comparator.naturalOrder()).get(); Integer min = stream.min(Comparator.naturalOrder()).get();
3.10 Match
Stream 有三个 match 方法,从语义上说:
allMatch:Stream 中全部元素符合传入的 predicate,返回 true
anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true
noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true
List<Person> persons = new ArrayList(); persons.add(new Person(1, "name" + 1, 10)); persons.add(new Person(2, "name" + 2, 21)); persons.add(new Person(3, "name" + 3, 34)); persons.add(new Person(4, "name" + 4, 6)); persons.add(new Person(5, "name" + 5, 55)); boolean isAllAdult = persons.stream(). allMatch(p -> p.getAge() > 18); System.out.println("All are adult? " + isAllAdult); boolean isThereAnyChild = persons.stream(). anyMatch(p -> p.getAge() < 12); System.out.println("Any child? " + isThereAnyChild);
Supplier 接口,你可以自己来控制流的生成。这种情形通常用于随机数、常量的 Stream,或者需要前后元素间维持着某种状态信息的 Stream.
generate 内部维护一个无限制的循环 根据传入的规则生成数据
需要使用Limit限制数据生成的范围
Random seed = new Random(); Supplier<Integer> random = seed::nextInt; Stream.generate(random).limit(10).forEach(System.out::println); //Another way IntStream.generate(() -> (int) (System.nanoTime() % 10000)). limit(10).forEach(System.out::println);
自定义Supplier
Stream<Person> stream2 = Stream.generate(new Use_Max_Min_Distinct().new PersonSupplier()).limit(10); stream2.forEach(System.out::println); private class PersonSupplier implements Supplier<Person>{ private Random random=new Random(); @Override public Person get() { return new Person("Tom",random.nextInt()); } } private class Person{ private String name; // Constructor get set toString() }
4.2 Iterator
iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。
然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。
Stream.iterate(0, n -> n + 3).limit(10). forEach(x -> System.out.print(x + " ")); // 0 3 6 9 12 15 18 21 24 27
在 iterate 时候管道必须有 limit 这样的操作来限制 Stream 大小。
List<Person> list = Arrays.asList( new Person("Tom1", 1), new Person("Tom2", 2), new Person("Tom3", 3), new Person("Tom4", 2), new Person("Tom5", 3), new Person("Tom6", 2) ); // 分类 Map<Integer, List<Person>> personGroups = Stream.generate(new PersonSupplier()).limit(10) .collect(Collectors.groupingBy(Person::getAge)); Stream<Entry<Integer, List<Person>>> stream = personGroups.entrySet().stream(); List<Entry<Integer, List<Person>>> list2 = stream.collect(Collectors.toList()); Iterator<Entry<Integer, List<Person>>> iterator = list2.iterator(); while(iterator.hasNext()) { Entry<Integer, List<Person>> entry = iterator.next(); System.out.println(entry.getKey()+" "+entry.getValue()); } // 按照断言划分 Map<Boolean, List<Person>> map = Stream.generate(new PersonSupplier()).limit(10) .collect(Collectors.partitioningBy(x->x.getAge()>5)); Stream<Entry<Boolean, List<Person>>> stream2 = map.entrySet().stream(); stream2.forEach((entry)->System.out.println(entry.getKey()+" "+entry.getValue())); class PersonSupplier implements Supplier<Person>{ public Person get() { return new Person("Tom"+new Random().nextInt(10) , new Random().nextInt(10)); }; } class Person {}// Constructor get set ToString()
原文:https://www.cnblogs.com/dgwblog/p/11748112.html