前言
stream流摒弃了传统便利集合的弊端,作为一个集合元素的函数模型,本身并不是一个集合,也不是数据结构,本身并不存储任何的元素或者其他地址值。Stream流的中间操作都会返回给流对象本身,多个操作可以串联成一个管道。
一、创建Stream流的三大基本步骤?
1.获取一个数据源
2.数据转换
3.执行操作获取想要的结果
ps:每次转换Stream对象不变,返回一个新的Stream对象。
二、获取流的方式
写在前面:
java.util.stream.Stream<T> 是Java 8新加入的最常用的流接口(这并不是一个函数式接口)。获取一个流非常简单,有以下几种常用的方式:
所有的 Collection 集合都可以通过 stream 默认方法获取流;
Stream 接口的静态方法 of 可以获取数组对应的流。
1.根据Collection获取流
在Java.util.Collection接口中加入了defult方法stream方法来获取流,因此其所有的实现类都可以获取流
代码如下(示例):
public class GetStream01 { public static void main(String[] args) { ArrayList<Object> list = new ArrayList<>(); Stream<Object> stream = list.stream(); HashSet<Object> set = new HashSet<>(); Stream<Object> stream1 = set.stream(); Vector<Object> vector = new Vector<>(); Stream<Object> stream2 = vector.stream(); } }
2.根据Map获取流
Map并不是Collection的子接口,加上Map的Key-Value结构不符合流元素单一的特征,因此在获取Map流时要分Key,Value,Entry等情况
代码如下(示例):
public class GetStream02 { public static void main(String[] args) { HashMap<String, String> map = new HashMap<>(); Stream<String> keystream = map.keySet().stream(); Stream<String> valueStream = map.values().stream(); Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream(); } }
3.根据数组获取流
在数组对象中不能添加默认方法,在Stream接口中提供了静态方法of (of方法其实就是一个可变参数,所以后面当然可以接数组啦)
代码如下(示例):
public class GetStream03 { public static void main(String[] args) { String[] array = {"肌肉猿","程序员非晚"}; Stream<String> stream = Stream.of(array); } }
三、常用方法列举
首先流模型的操作很丰富,常用的API分为两大类
- 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
- 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。本文章中终结方法包括 count 和 forEach 方法。
1.逐一处理:forEach
public class StreamForEach { public static void main(String[] args) { String[] array = {"肌肉猿","程序员非晚"}; Stream<String> stream = Stream.of(array); stream.forEach(name-> System.out.println(name)); } } ------------------------------------------------------------------------------------------------------------------- void forEach(Consumer<? super T> action); -------------------------------------------------------------------------------------------------------------------- @FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @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); }; } }
通过查看foreach的细节,该方法接收一个Consumer接口函数,从而将每一个流元素交给该函数处理。而在Java.util.function.Consumer接口作为一个消费型接口,在接口中包含了一个抽象方法accept,消费一个指定泛型的数据。
2.过滤:filter
public class StreamForEach { public static void main(String[] args) { String[] array = {"肌肉猿","程序员非晚","程序员爱写Java"}; Stream<String> stream = Stream.of(array); Stream<String> stringStream = stream.filter(s -> s.startsWith("程")); } } ----------------------------------------------------------------------------------------------------------------------- Stream<T> filter(Predicate<? super T> predicate); ----------------------------------------------------------------------------------------------------------------------- @FunctionalInterface public 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); /** * Returns a composed predicate that represents a short-circuiting logical * AND of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code false}, then the {@code other} * predicate is not evaluated. * * <p>Any exceptions thrown during evaluation of either predicate are relayed * to the caller; if evaluation of this predicate throws an exception, the * {@code other} predicate will not be evaluated. * * @param other a predicate that will be logically-ANDed with this * predicate * @return a composed predicate that represents the short-circuiting logical * 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); } /** * Returns a predicate that represents the logical negation of this * predicate. * * @return a predicate that represents the logical negation of this * predicate */ default Predicate<T> negate() { return (t) -> !test(t); } /** * Returns a composed predicate that represents a short-circuiting logical * OR of this predicate and another. When evaluating the composed * predicate, if this predicate is {@code true}, then the {@code other} * predicate is not evaluated. * * <p>Any exceptions thrown during evaluation of either predicate are relayed * to the caller; if evaluation of this predicate throws an exception, the * {@code other} predicate will not be evaluated. * * @param other a predicate that will be logically-ORed with this * predicate * @return a composed predicate that represents the short-circuiting logical * OR of this predicate and the {@code other} predicate * @throws NullPointerException if other is null */ default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } /** * Returns a predicate that tests if two arguments are equal according * to {@link Objects#equals(Object, Object)}. * * @param <T> the type of arguments to the predicate * @param targetRef the object reference with which to compare for equality, * which may be {@code null} * @return a predicate that tests if two arguments are equal according * to {@link Objects#equals(Object, Object)} */ static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
通过上面的代码观察我们可以得知,filter方法将一个流转化为另一个子集流,该接口接收一个Predicate接口参数,通过查看其内部我们不难发现该接口是一个函数式接口,因此可以使用Lambda表达式作为筛选条件。
3.映射:map
public class StreamMap { public static void main(String[] args) { Stream<String> original = Stream.of("2023","02","14"); Stream<Integer> result = original.map(s -> Integer.parseInt(s)); } } ------------------------------------------------------------------------------------------------------------------- <R> Stream<R> map(Function<? super T, ? extends R> mapper); -------------------------------------------------------------------------------------------------------------------- @FunctionalInterface 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); /** * Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of input to the {@code before} function, and to the * composed function * @param before the function to apply before this function is applied * @return a composed function that first applies the {@code before} * function and then applies this function * @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)); } /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @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)); } /** * Returns a function that always returns its input argument. * * @param <T> the type of the input and output objects to the function * @return a function that always returns its input argument */ static <T> Function<T, T> identity() { return t -> t; } }
通过上述代码观察,我们可以通过map映射(就是描述一种将T类型数据转化为R类型数据的操作)的方式将一个流中的元素映射到另一个流中,本质是通过接口中的Function函数,将当前流中的T类型数据转化为R类型数据。函数的内部又刚好是一个函数式接口。
4.取用前几个:limit
public class StreamLimit { public static void main(String[] args) { Stream<String> stringStream = Stream.of("肌肉猿", "程序员非晚", "爱Java爱分享","肌肉猿", "程序员非晚", "爱Java爱分享"); Stream<String> stream = stringStream.limit(2); System.out.println(stream.count());//2 } } ----------------------------------------------------------------------------------------------------------------------- Stream<T> limit(long maxSize);
5.跳过前几个:skip
public class StreamSkip { public static void main(String[] args) { Stream<String> stringStream = Stream.of("肌肉猿", "程序员非晚", "爱Java爱分享","肌肉猿", "程序员非晚", "爱Java爱分享"); Stream<String> stream = stringStream.skip(2); System.out.println(stream.count());//4 } } ----------------------------------------------------------------------------------------------------------------------- Stream<T> skip(long n);
6.组合:concat
public class StreamConcat { public static void main(String[] args) { Stream<String> stream = Stream.of("肌肉猿"); Stream<String> stream1 = Stream.of("程序员非晚"); Stream<String> result = Stream.concat(stream, stream1); } } ----------------------------------------------------------------------------------------------------------------------- public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) { Objects.requireNonNull(a); Objects.requireNonNull(b); @SuppressWarnings("unchecked") Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>( (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator()); Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel()); return stream.onClose(Streams.composedClose(a, b)); }
将两个流合并为一个流,注意本方法和string中的concat方法不同
附上String中的concat源码
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); }