java8——Stream API
来源:互联网 发布:利用淘宝客赚钱 编辑:程序博客网 时间:2024/04/27 20:16
2.1 从迭代器到Stream操作
List<String> words = ...;for(String w: words){ if(w.length()>12) count++;}
普通的迭代器,很难被并行计算。这也是java8引入大量操作符的原因。
List<String> s = new ArrayList<>();long count = s.stream().filter(w -> w.length()>12).count();
注意,只有List< T >中的泛型确定,w里方法才不会报错。
stream方法会为单词列表生成一个Stream。filter方法会返回另一个只包含单词长度大于12的Stream。count方法会将Stream化简成一个结果。
1.Stream自己不会存储元素。元素可能被存储在底层的集合中。
2.Stream操作符不会改变原对象,相反,它们会返回一个持有结果的新的Stream。
3.Stream可能是延迟执行的。
以下是并行统计的写法:
long count = s.parallelStream().filter(w -> w.length()>12).count();
使用流水线:
- 创建Stream(流) (2.2)
- 在一个或多个步骤中,将初始Stream转化为另一个Stream的中间操作,如filter (2.3-2.5)
- 终止操作,会强制它之前的延迟操作立即执行。如count(2.6)
数据流操作要么是衔接操作,要么是终止操作。衔接操作返回数据流,所以我们可以把多个衔接操作不使用分号来链接到一起。终止操作无返回值,或者返回一个不是流的结果。在上面的例子中,filter、map和sorted都是衔接操作,而forEach是终止操作。列你在上面例子中看到的这种数据流的链式操作也叫作操作流水线。
多数数据流操作都接受一些lambda表达式参数,函数式接口用来指定操作的具体行为。这些操作的大多数必须是无干扰而且是无状态的。它们是什么意思呢?
当一个函数不修改数据流的底层数据源,它就是无干扰的。例如,在上面的例子中,没有任何lambda表达式通过添加或删除集合元素修改myList。
当一个函数的操作的执行是确定性的,它就是无状态的。例如,在上面的例子中,没有任何lambda表达式依赖于外部作用域中任何在操作过程中可变的变量或状态。
2.2 创建Stream
java8在Collection接口中添加了stream方法,可以将任何一个集合转化为Stream。
如果是数组,也可以用静态的Stream.of方法得到一个Stream。
Stream<String> song = Stream.of("a","b","c");
empty(),创建一个不含任何元素的Stream
public static<T> Stream<T> empty() { return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false); }
2.3 filter map flatMap方法
流转换是指从一个流中读取数据,并将转换后的数据写入到另一个流中。
filter方法:顾名思义,过滤,并得到一个满足要求的新Stream
Stream<T> filter(Predicate<? super T> predicate);//所以这里可以用lambda表达式@FunctionalInterfacepublic interface Predicate<T> { /** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t);
map方法:对流中的元素进行某种形式的转换,但是输出是以流对象为基本单位(?)
Stream<String> lowercaseWords = s.stream().map(String::toLowerCase);<R> Stream<R> map(Function<? super T, ? extends R> mapper);public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t);
flatMap:以元素为基本单位,合并成一个流再返回
public static Stream<Character> characterStream(String s){ ...}Stream<Stream<Character>> temp1 = s.stream().map(w-> characterStream(w)); //只能输出一个包含多个流的流Stream<Character> letters = s.stream().flatMap(w-> characterStream(w)); //可以输出一个只包含字符串的流
2.4 提取子流和组合流
limit(long maxSize) 裁剪出指定长度的流
Stream<Double> sa = Stream.generate(Math::random).limit(100);public final Stream<P_OUT> limit(long maxSize) { if (maxSize < 0) throw new IllegalArgumentException(Long.toString(maxSize)); return SliceOps.makeRef(this, 0, maxSize); }
Stream< T > skip(long n); 丢弃前面的n个元素
2.5 有状态的流转换
无状态的流转换:指流获取一个元素时,并不依赖于之前的状态。
有状态的转换:流必须记住之前已读取的元素。
Stream<String> uqi = Stream.of("m","m","n").distinct();//去重Stream<String> longest = uqi.sorted(Comparator.comparing(String::length).reversed());//排序
2.6 简单的聚合方法
聚合方法就是终止操作。可以理解成对流的最后一个操作(?)
max(),min(),findAny(),anyMatch()….
值得注意的是,这些方法会返回一个Optional< T >值,它可能会封装返回值,也可能表示没有返回值。在使用java其他方法时,通常会抛出null,有可能会抛出空指针异常。所以Optional类型是一种更好的表示缺少返回值的方式。
2.7 Optional类型
2.7.1 正确使用Optional值
或者接受正确值,或者返回一个替代值。Optional类型可以更加简便地完成。
/** * Return the value if present, otherwise return {@code other}. * * @param other the value to be returned if there is no value present, may * be null * @return the value, if present, otherwise {@code other} */ public T orElse(T other) { return value != null ? value : other; }
也有更高级的返回替代值的方法
/** * Return the value if present, otherwise invoke {@code other} and return * the result of that invocation. * * @param other a {@code Supplier} whose result is returned if no value * is present * @return the value if present otherwise the result of {@code other.get()} * @throws NullPointerException if value is not present and {@code other} is * null */ public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); }
2.7.2 创建可选值
Optional.ofNullable(obj)
2.7.3 使用flatMap来组合可选值函数
2.8 聚合操作
Stream<Integer> values = Stream.of(1,2,3,5);Optional<Integer> sum = values.reduce((x,y)->x+y);
可以得到所有值的和。
一般来说聚合方法有一个聚合操作op,该聚合会产生v1 op v2 op v3…op vn的值。
该操作应该是联合的,如求和,求积(计算顺序不影响计算结果)
减法就不是一个联合操作
另一种形式:
Integer sum2 = values.reduce(0,(x,y)->x+y);
找到标识符e,使得e op x = x,即可使用另一种形式的聚合操作。
2.9 收集结果
用于查看结果:
Object[] toArray(); //Stream里的元素封装成数组,并返回
针对并行的过程,使用collect方法
HashSet<String> set = uqi.collect(HashSet::new,HashSet::add,HashSet::addAll);
uqi是一个Stream。
三个参数分别为:
1. 一个能创建目标类型实例的方法,例如hash set的构造函数
2. 一个能将元素添加到目标中的方法,例如一个add方法
3. 一个将两个对象整合到一起的方法,例如addAll方法
collect方法可以调用Collector类中的很多方法,很实用
tip:
void forEach(Consumer< ? super T> action);
遍历流元素并执行相关操作,但这是一个终止操作。
如不想终止,可以用peek
2.10 将结果收集到Map中
仍然是使用collect方法,方法中调用Collectors中的toMap方法,toMap返回了一个接口
Map<Integer, String> idToName = people.collect(Collenctor.toMap(Person::getId, Person::getName));假设people是一个Stream<Person>对象
也有toCurrentHashMap方法,用来生成一个并发的map
2.11 分组和分片
对具有相同特性的值进行分组,直接使用groupingBy
Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());Map<String, List<Locale>> lo = locales.collect(Collectors.groupingBy(Locale::getCountry));List<Locale> swiss = lo.get("CH");
当分类函数是一个predicate函数时,流元素会被分成两组列表。这时,使用partitioningBy更有效率。
java8还提供了其他一些收集器,用来对分组后的元素进行downstream处理。
以下,是不同参数groupingBy最终调用的方法。
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream) { Supplier<A> downstreamSupplier = downstream.supplier(); BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator(); BiConsumer<Map<K, A>, T> accumulator = (m, t) -> { K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); A container = m.computeIfAbsent(key, k -> downstreamSupplier.get()); downstreamAccumulator.accept(container, t); }; BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner()); @SuppressWarnings("unchecked") Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory; if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) { return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID); } else { @SuppressWarnings("unchecked") Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher(); Function<Map<K, A>, M> finisher = intermediate -> { intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v)); @SuppressWarnings("unchecked") M castResult = (M) intermediate; return castResult; }; return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID); } }
2.12 原始类型流
Stream API中:IntStream可以存储short,char,byte和boolean类型的值,DoubleStream可以存储float型。
接口中封装了一些求和,求平均,求最大值之类的方法。
2.13 并行流
流使得并行计算变得容易。
parallel()可以将任意的串行流转换为并行流。
Stream<country> countryStream = Stream.of(China).parallel();
需要确保传递给并行流操作的函数是线程安全的。
2.14 函数式接口
总结Stream的适用场景:
- 集合操作超过两个步骤
比如先filter再for each
这时Stream显得优雅简洁,效率也高 - 任务较重,注重效能,希望并发执行
很容易的就隐式利用了多线程技术。非常好的使用时机。 - 函数式编程的编码风格里
Stream的设计初衷之一
- java8——Stream API
- Java8学习笔记 — 【Stream API】
- JAVA8新特性(四)——Stream API
- Java8 Stream API介绍
- JAVA8 Stream API 入门
- JAVA8 Stream API 进阶
- java8 Stream API
- java8-02-Stream-API
- java8之Stream-API
- java8 Stream API初识
- java8 Stream API笔记
- Java8 Stream API介绍
- Java8 Stream API
- java8之Stream API
- Java8 Stream API
- Java8 Stream API使用
- Java8 Stream API学习笔记
- java8 Stream API之reduce
- ActivityThread与ApplicationThread
- 计算思维的结构 | 单元测验1
- filter与拦截器的区别
- innodb buffer pool管理--LRU young何时make old
- 关于group by 、group by having、where group by与 group by order by
- java8——Stream API
- CALayer关闭隐式动画
- java邮件的发送
- 关于js函数传入中文字符串参数的取值问题
- 如何让 height:100%; 起作用
- 二叉树
- web实现地图画标识物
- 7. Linux 环境
- apktool官网及最新版本