Stream流

简介: Stream流

前言

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);
    }

目录
相关文章
|
2月前
|
存储 JavaScript 网络协议
Stream
【10月更文挑战第22天】
35 1
|
3月前
|
Java 数据处理
Stream流的简单使用
这篇文章介绍了Java中Stream流的基本概念和使用方法。文章解释了Stream流的三类方法:获取流、中间方法和终结方法。详细讨论了如何生成Stream流,包括从Collection体系集合、Map体系集合、数组和同种数据类型的多个数据中生成流。接着,介绍了Stream流的中间操作方法,如`filter`、`limit`、`skip`、`concat`和`distinct`。文章还讨论了Stream流的终结方法,如`forEach`和`count`,以及收集方法,如`collect`。最后,通过几个例子演示了如何使用Stream流进行数据处理和收集操作。
|
7月前
|
Java 容器
Stream 流常见基本操作
Stream 流常见基本操作
|
6月前
|
API
Stream流知识
Stream流知识
40 0
|
6月前
|
存储 Java API
Java——Stream流(1/2):Stream流入门、Stream流的创建(认识Stream、体验Stream流、Stream流的使用步骤、获取Stream流的方法)
Java——Stream流(1/2):Stream流入门、Stream流的创建(认识Stream、体验Stream流、Stream流的使用步骤、获取Stream流的方法)
100 0
java流是指在Java中用来读写数据的一组有序的数据序列,它可以将数据从一个地方带到另一个地方。java流分为输入流和输出流,输入流是从源读取数据的流,而输出流是将数据写入到目的地的流。Java流又可以分为字节流和字符流,字节流读取的最小单位是一个字节(1byte=8bit),而字符流一次可以读取一个字符(1char = 2byte = 16bit)。Java流还可以分为节点流和处理流,节点流是直接从一个源读写数据的流(这个流没有经过包装和修饰),处理流是在对节点流封装的基础上的一种流。
129 0
|
Java
stream流操作
stream流操作
82 0
|
7月前
Stream流
Stream流
81 1
|
7月前
|
Java
Stream流教程
Stream流教程
82 0