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

相关文章
|
测试技术
12 Mac 下MQTT免费测试工具MQTTBox
12 Mac 下MQTT免费测试工具MQTTBox
1166 0
|
8月前
|
Oracle Java 关系型数据库
Java命名规范
Java命名规范涵盖包、类、方法、变量等命名规则。包名全小写,类名首字母大写采用驼峰法,接口常用形容词,抽象类以Abstract/Base开头,异常类以Exception结尾,方法名小写驼峰,常量全大写用下划线分隔,枚举值按常量规范命名,提升代码可读性与一致性。
1131 0
|
10月前
|
SQL Oracle 关系型数据库
Oracle 数据库数据操作:精通 INSERT, UPDATE, DELETE
在Oracle里,增加数据用INSERT,修改用UPDATE,删除则用DELETE。进行修改和删除时,建议总是带上WHERE条件来指定范围,这样可以确保操作的准确性
1622 4
|
人工智能 机器人 数据库
使用FlowiseAI轻松搭建AI驱动的交互式应用
FlowiseAI 是一款开源低代码工具,旨在帮助开发者构建自定义的语言学习模型应用。它提供拖放界面,支持与多种AI模型和数据库集成,适用于创建聊天机器人等交互式应用。使用阿里云的计算巢,用户可通过一键部署快速启动FlowiseAI,并通过简单的步骤配置和运行自定义的LLM应用。
|
存储 Java 数据库连接
Spring Boot 配置主从数据库实现读写分离
Spring Boot 配置主从数据库实现读写分离
2276 0
|
Java 测试技术
[软件自动化测试] selenium java环境配置
介绍了包括选择Gradle构建,关闭Gradle Wrapper的默认下载,修改`gradle-wrapper.properties`以使用腾讯云镜像,以及更新`build.gradle`文件添加国内仓库和Selenium依赖。在测试驱动部分,创建了一个简单的JUnit测试用例,展示了如何初始化Edge浏览器并访问网站。运行测试时可能出现关于CDP版本不匹配的警告,可以通过升级Selenium依赖中的CDP版本来解决。推荐根据[Selenium库网页](https://search.maven.org/search?q=g:org.seleniumhq.selenium)提供的版本信息进行升级
2635 6
|
网络协议 中间件 机器人
通过顶顶通呼叫中心中间件玩转FreeSWITCH媒体流
怎么获取FreeSWITCH的媒体流是一个老生常谈的问题了,最常见的方法media_bug 很多人需要直接获取原始的声音流,然后自己处理,其实FreeSWITCH也是支持的,只是知道的极少,FreeSWITCH原生支持 unicast 通过udp 把声音流推送到一个UDP端口,和播放网络的声音流。 fs实现的unicast,有一个缺陷如果启动了unicast 就没办法调用playback等放音函数了。
1605 0
|
XML Java 关系型数据库
MyBatis-Plus分页插件的配置和使用
MyBatis-Plus分页插件的配置和使用
2331 0
|
测试技术
当技术宅遇上技术债:你见,或者不见 债就在那里
技术债务是由Ward Cunningham在1992年创造的一个比喻,被定义为当我们有意或无意地做了错误的或不理想的技术决策所累积的债务。本文就由当过15年IT民工的技术宅为大家分享关于技术债的那些故事。
3886 0

热门文章

最新文章