Stream流
函数式接口
1.消费型接口——Consumer
@FunctionalInterface
public interface Consumer<T> {
/**
* 对给定的参数执行此操作。
*
* @param t 输入参数
*/
void accept(T t);
/**
* 返回一个组合的Consumer , Consumer执行该操作,后跟after操作。 如果执行任一操作会抛出异常,
* 它将被转发到组合操作的调用者。 如果执行此操作抛出一个异常, after操作将不被执行。
*
* @param after 此操作后执行的操作
* @return 一个组合的 Consumer按顺序执行该操作,后跟 after操作
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
2.供给型接口——Supplier
@FunctionalInterface
public interface Supplier<T> {
/**
* 获取一个结果
*
* @return 返回一个结果
*/
T get();
}
3.函数型接口——Function
@FunctionalInterface
public interface Function<T, R> {
/**
* 将此函数应用于给定的参数。
*
* @param t 函数参数
* @return 功能结果
*/
R apply(T t);
/**
* 返回一个组合函数,首先将before函数应用于其输入,然后将此函数应用于结果。
* 如果任一函数的评估引发异常,则将其转发给组合函数的调用者。
*
* @param <V> 输入到 before函数的类型,并且组合函数
* @param before 应用此功能之前应用的功能
* @return 一个组合函数首先应用 before函数,然后应用此功能异常
* @throws NullPointerException if before is null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 返回一个组合函数,首先将此函数应用于其输入,然后将after函数应用于结果。
* 如果任一函数的评估引发异常,则将其转发给组合函数的调用者。
*
* @param <V> after功能的输出类型,以及组合功能
* @param 应用此函数后应用的功能
* @return 一个组合函数首先应用此函数,然后应用 after函数
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* 返回一个总是返回其输入参数的函数。
*
* @param <T> 函数的输入和输出对象的类型
* @return 一个总是返回其输入参数的函数
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
4.断言型接口——Predicate
@FunctionalInterface
public interface Predicate<T> {
/**
* 在给定的参数上评估这个谓词。
*
* @param t 输入参数
* @return true如果输入参数匹配该谓词,否则为 false
* otherwise {@code false}
*/
boolean test(T t);
/**
* 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND。
* 当评估组合谓词时,如果此谓词为false ,则不other other谓词。
* 在评估任一谓词期间抛出的任何异常被中继到调用者;
* 如果此断言的评价抛出一个异常, other断言不会被评估。
*
* @param other 将与此谓词进行逻辑与AND的谓词
* @return 一个代表该谓词和other谓词的短路逻辑AND的 other谓词
* AND of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* 返回表示此谓词的逻辑否定的谓词。
*
* 一个表示该谓词的逻辑否定的谓词
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或。
* 当评估组合谓词时,如果此谓词为true ,则不other other谓词。
* 在评估任一谓词期间抛出的任何异常被中继到调用者;
* 如果此断言的评价抛出一个异常, other断言不会被评估。
*
* @param other 将与此谓词进行逻辑关系的谓词
* @return 表示短路逻辑这个谓词的或组成谓词和 other谓词
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* 返回测试,如果两个参数按照相等谓词 Objects.equals(Object, Object) 。
*
* @param <T> T的参数类型
* @param 用于比较相等的对象引用,可能是 null
* @return 根据 Objects.equals(Object, Object)测试两个参数是否相等的 谓词
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Stream流
1.创建流
-
集合创建
List<User> list = getList(); //直接使用集合的stream方法获取stream流 Stream<User> stream = list.stream();
-
数组创建
int[] array = {1,2,3,4,5,6}; //使用数组工具类的stream方法转化 IntStream stream1 = Arrays.stream(array); //使用Stream接口的of方法转换 Stream<int[]> stream2 = Stream.of(array);
2.流中间操作
-
filter
filter中传入了一个参数——断言型接口,意味着实际调用中可以使用匿名内部类(lanbda)的方式将过滤的实现细节定义出来。
/** * 返回由与此给定谓词匹配的此流的元素组成的流。 * * 这是一个intermediate operation 。 * * @param predicate 一个 non-interfering , stateless谓词应用到每个元素,以确定是否它应包含 * @return 新的流 */ Stream<T> filter(Predicate<? super T> predicate);
应用:
//筛选年龄大于20的用户 stream.filter(new Predicate<User>() { @Override public boolean test(User user) { return user.getAge() > 20; } }); //lambda使用,后续一律使用lambda表达式举例 //筛选用户姓名为小黑子的用户 stream.filter(user->user.getName().equals("小黑子"));
-
map
map中传入一个函数型接口,旨在将T类型的对象转换为R型的对象。在实际使用中定义具体转化细节,可以是属性提取、类型转换或者计算逻辑等。在lambda表达式中,参数R可省略,在返回值中体现即可。
/** * 返回由给定函数应用于此流的元素的结果组成的流。 * * 这是一个intermediate operation 。 * * @param <R> 新流的元素类型 * @param mapper 一个 non-interfering , stateless函数应用到每个元件 * @return 新的流 */ <R> Stream<R> map(Function<? super T, ? extends R> mapper);
应用:
//返回每个对象中的用户名 stream.map(user -> user.getName()); //对于一些纯功能性的定义,比如输出、get方法,可以进一步省略 stream.map(User::getName); //批量将数字转化为字符串 stream.map(num -> String.valueOf(num));
-
distinct
流元素去重
distinct依赖Object中的equals方法,如果需要自定义判重逻辑,需要重写equals方法
/** * 返回由该流的不同元素(根据Object.equals(Object) )组成的流。 * 对于有序流,选择不同的元素是稳定的(对于重复的元素 * 首先在遇到顺序中出现的元素被保留。) * 对于无序流,不能保证稳定性。 * 这是一个stateful intermediate operation 。 * * @apiNote * 保存稳定性为distinct()在并行管线是相对昂贵的 * (要求操作充当一个完整屏障,具有大量缓冲的开销),并且稳定性通常是不需要的。 * 使用无序流源(如generate(Supplier) )或具有除去排序约束 * BaseStream.unordered()可相比distinct()更加更高效,如果语法允许的话 * 如果需要与遇到顺序一致, distinct()在并行流水线中使用distinct()您的性能或内存利用率不佳, * 则使用BaseStream.sequential()切换到顺序执行可能会提高性能。 * * @return the new stream */ Stream<T> distinct();
-
sorted
对流元素进行排序
流元素如果为复杂类型,需要实现comparable接口重写compareTo方法或者传入一个comparable匿名内部类重写compareTo方法。
/** * 返回由此流的元素组成的流,根据自然顺序排序。 * 如果该流的元件不是Comparable ,一个java.lang.ClassCastException执行终端操作时,可以抛出。 * 对于有序流,排序稳定。 对于无序的流,不能保证稳定性。 * 这是一个stateful intermediate operation 。 * * @return 新的流 */ Stream<T> sorted(); /** * @param comparator 一个 non-interfering,stateless Comparator被用于比较流元素 * @return the new stream */ Stream<T> sorted(Comparator<? super T> comparator);
使用:
//实现Comparable接口 public class User implements Comparable{ ... @Override public int compareTo(Object o) { //比较逻辑 } } //传入Comparable参数 stream.sorted((o1, o2) -> {//判断逻辑 });
-
limit
限制输出流元素个数,超出limit的元素将会被抛弃。
/** * 返回由该流的元素组成的流,截断长度不能超过maxSize 。 * 这是一个short-circuiting stateful intermediate operation 。 * * @apiNote * 虽然limit()通常是在连续的流管道的廉价的操作,它可是并行管道中相当昂贵的, * 特别是对于大的值maxSize ,由于limit(n)被约束返回不是任何n个元素,但在遭遇顺序中的第n个元素。 * 使用无序流源(如generate(Supplier) )或去除所述排序约束与BaseStream.unordered() * 可相比limit()在并行管道获得显著加速,如果语法允许的话。 * 如果需要与遇到顺序一致, limit()在并行流水线中遇到limit()的性能下降或内存利用率下降, * 则使用BaseStream.sequential()切换到顺序执行可能会提高性能。 * * @param maxSize 流应该限制的元素数量 * @return 新的流 * @throws IllegalArgumentException 如果 maxSize为负数 */ Stream<T> limit(long maxSize);
使用:
//限制最大长度为2 stream.limit(2);
-
skip
跳过前n个元素,通常是在排序后使用
使用:
//打印除了年龄最大用户之外的其他用户 stream.sorted() .skip(1)
-
flatMap
相比map方法,flatMap可以将一个元素转化为多个流中的元素。比如需要从一个用户列表中获取到用户的爱好列表。map取出来的是一个List元素的stream流。如果需要取出以爱好对象为元素的stream流,就可以使用flatMap。
/** * 返回由通过将提供的映射函数应用于每个元素而产生的映射流的内容来替换该流的每个元素的结果的流。 * 每个映射的流在其内容被放入此流之后是closed 。 (如果映射的流是null则使用空的流)。 * * 这是一个intermediate operation 。 * * @apiNote * flatMap()操作具有对流的元素应用一对多变换,然后将所得到的元素平坦化为新流的效果。 */ <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
可以发现,flatMap和map很像,所以map理论上也可以实现flatMap一对多的效果。
stream.map(user -> user.getHobby().stream()); stream.flatMap(user -> user.getHobby().stream());
实际上是这样吗,不是的,我们通过调试可以发现,map返回的是stream的嵌套流,而flatMap返回的是hobby对象流
也就是说,flatMap帮我们自动做了流拼接,在实际使用中需要注意区分使用场景。