【Java8新特性】Stream API有哪些中间操作?看完你也可以吊打面试官!!

简介: 作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:https://github.com/sunshinelyz/mykit-delayPS: 欢迎各位Star源码,也可以pr你牛逼哄哄的代码。

Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值” 。Stream的中间操作是不会有任何结果数据输出的。

Stream的中间操作在整体上可以分为:筛选与切片、映射、排序。接下来,我们就分别对这些中间操作进行简要的说明。

筛选与切片

这里,我将与筛选和切片有关的操作整理成如下表格。


接下来,我们列举几个简单的示例,以便加深理解。

为了更好的测试程序,我先构造了一个对象数组,如下所示。

protected List<Employee> list = Arrays.asList(
    new Employee("张三", 18, 9999.99),
    new Employee("李四", 38, 5555.55),
    new Employee("王五", 60, 6666.66),
    new Employee("赵六", 8, 7777.77),
    new Employee("田七", 58, 3333.33)
);

其中,Employee类的定义如下所示。

@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {
    private static final long serialVersionUID = -9079722457749166858L;
    private String name;
    private Integer age;
    private Double salary;
}

Employee类的定义比较简单,这里,我就不赘述了。之后的示例中,我们都是使用的Employee对象的集合进行操作。好了,我们开始具体的操作案例。

1.filter()方法

filter()方法主要是用于接收Lambda表达式,从流中排除某些元素,其在Stream接口中的源码如下所示。

Stream<T> filter(Predicate<? super T> predicate);

可以看到,在filter()方法中,需要传递Predicate接口的对象,Predicate接口又是个什么鬼呢?点进去看下源码。

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    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);
    }
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

可以看到,Predicate是一个函数式接口,其中接口中定义的主要方法为test()方法,test()方法会接收一个泛型对象t,返回一个boolean类型的数据。

看到这里,相信大家明白了:filter()方法是根据Predicate接口的test()方法的返回结果来过滤数据的,如果test()方法的返回结果为true,符合规则;如果test()方法的返回结果为false,则不符合规则。

这里,我们可以使用下面的示例来简单的说明filter()方法的使用方式。

//内部迭代:在此过程中没有进行过迭代,由Stream api进行迭代
//中间操作:不会执行任何操作
Stream<Person> stream = list.stream().filter((e) -> {
    System.out.println("Stream API 中间操作");
    return e.getAge() > 30;
});

我们,在执行终止语句之后,一边迭代,一边打印,而我们并没有去迭代上面集合,其实这是内部迭代,由Stream API 完成。

下面我们来看看外部迭代,也就是我们人为得迭代。

//外部迭代
Iterator<Person> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

2.limit()方法

主要作用为:截断流,使其元素不超过给定数量。

先来看limit方法的定义,如下所示。

Stream<T> limit(long maxSize);

limit()方法在Stream接口中的定义比较简单,只需要传入一个long类型的数字即可。

我们可以按照如下所示的代码来使用limit()方法。

//过滤之后取2个值
list.stream().filter((e) -> e.getAge() >30 ).limit(2).forEach(System.out :: println);

在这里,我们可以配合其他得中间操作,并截断流,使我们可以取得相应个数得元素。而且在上面计算中,只要发现有2条符合条件得元素,则不会继续往下迭代数据,可以提高效率。

3.skip()方法

跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补。

源码定义如下所示。

Stream<T> skip(long n);

源码定义比较简单,同样只需要传入一个long类型的数字即可。其含义是跳过n个元素。

简单示例如下所示。

//跳过前2个值
list.stream().skip(2).forEach(System.out :: println);

4.distinct()方法

筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素。

源码定义如下所示。

Stream<T> distinct();

旨在对流中的元素进行去重。

我们可以如下面的方式来使用disinct()方法。

list.stream().distinct().forEach(System.out :: println);

这里有一个需要注意的地方:distinct 需要实体中重写hashCode()和 equals()方法才可以使用。

映射

关于映射相关的方法如下表所示。

微信图片_20211118180735.jpg

1.map()方法

接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。

先来看Java8中Stream接口对于map()方法的声明,如下所示。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

我们可以按照如下方式使用map()方法。

//将流中每一个元素都映射到map的函数中,每个元素执行这个函数,再返回
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);
//获取Person中的每一个人得名字name,再返回一个集合
List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList());

2.flatMap()

接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流。

先来看Java8中Stream接口对于flatMap()方法的声明,如下所示。

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

我们可以使用如下方式使用flatMap()方法,为了便于大家理解,这里,我就贴出了测试flatMap()方法的所有代码。

/**
     * flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流
     */
    @Test
    public void testFlatMap () {
        StreamAPI_Test s = new StreamAPI_Test();
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
        list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);
        //如果使用map则需要这样写
        list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
            e.forEach(System.out::println);
        });
    }
    /**
     * 将一个字符串转换为流
     */
    public Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }

其实map方法就相当于Collaction的add方法,如果add的是个集合得话就会变成二维数组,而flatMap 的话就相当于Collaction的addAll方法,参数如果是集合得话,只是将2个集合合并,而不是变成二维数组。

排序

关于排序相关的方法如下表所示。

从上述表格可以看出:sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。

先来看Java8中Stream接口对于sorted()方法的声明,如下所示。

Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);

sorted()方法的定义比较简单,我就不再赘述了。

我们也可以按照如下方式来使用Stream的sorted()方法。

// 自然排序
List<Employee> persons = list.stream().sorted().collect(Collectors.toList());
//定制排序
List<Employee> persons1 = list.stream().sorted((e1, e2) -> {
    if (e1.getAge() == e2.getAge()) {
        return 0;
    } else if (e1.getAge() > e2.getAge()) {
        return 1;
    } else {
        return -1;
    }
}).collect(Collectors.toList());
相关文章
|
3天前
|
存储 安全 Java
说说Java 8 引入的Stream API
说说Java 8 引入的Stream API
8 0
|
3天前
|
分布式计算 Java API
Java 8新特性之Lambda表达式与Stream API
【4月更文挑战第16天】本文将介绍Java 8中的两个重要新特性:Lambda表达式和Stream API。Lambda表达式是Java 8中引入的一种新的编程语法,它允许我们将函数作为参数传递给其他方法,从而使代码更加简洁、易读。Stream API是Java 8中引入的一种新的数据处理方式,它允许我们以声明式的方式处理数据,从而使代码更加简洁、高效。本文将通过实例代码详细讲解这两个新特性的使用方法和优势。
|
7天前
|
前端开发 Oracle Java
Java 22 新增利器: 使用 Java Stream Gather 优雅地处理流中的状态
Java 22 新增利器: 使用 Java Stream Gather 优雅地处理流中的状态
17 0
|
7天前
|
存储 Java 关系型数据库
掌握Java 8 Stream API的艺术:详解流式编程(一)
掌握Java 8 Stream API的艺术:详解流式编程
37 1
|
10天前
|
缓存 前端开发 API
API接口封装系列
API(Application Programming Interface)接口封装是将系统内部的功能封装成可复用的程序接口并向外部提供,以便其他系统调用和使用这些功能,通过这种方式实现系统之间的通信和协作。下面将介绍API接口封装的一些关键步骤和注意事项。
|
17天前
|
监控 前端开发 JavaScript
实战篇:商品API接口在跨平台销售中的有效运用与案例解析
随着电子商务的蓬勃发展,企业为了扩大市场覆盖面,经常需要在多个在线平台上展示和销售产品。然而,手工管理多个平台的库存、价格、商品描述等信息既耗时又容易出错。商品API接口在这一背景下显得尤为重要,它能够帮助企业在不同的销售平台之间实现商品信息的高效同步和管理。本文将通过具体的淘宝API接口使用案例,展示如何在跨平台销售中有效利用商品API接口,以及如何通过代码实现数据的统一管理。
|
29天前
|
安全 算法 API
产品经理必备知识——API接口
前言 在古代,我们的传输信息的方式有很多,比如写信、飞鸽传书,以及在战争中使用的烽烟,才有了著名的烽火戏诸侯,但这些方式传输信息的效率终究还是无法满足高速发展的社会需要。如今万物互联的时代,我通过一部手机就可以实现衣食住行的方方面面,比如:在家购物、远程控制家电、自动驾驶等等,背后都离不开我们今天要聊的API接口。
|
29天前
|
数据采集 JSON API
如何实现高效率超简洁的实时数据采集?——Python实战电商数据采集API接口
你是否曾为获取重要数据而感到困扰?是否因为数据封锁而无法获取所需信息?是否因为数据格式混乱而头疼?现在,所有这些问题都可以迎刃而解。让我为大家介绍一款强大的数据采集API接口。
|
1月前
|
安全 API 数据安全/隐私保护
API接口知识小结
应用程序接口API(Application Programming Interface),是提供特定业务输出能力、连接不同系统的一种约定。这里包括外部系统与提供服务的系统(中后台系统)或后台不同系统之间的交互点。包括外部接口、内部接口,内部接口又包括:上层服务与下层服务接口、同级接口。
|
4天前
|
人工智能 API 开发者
免费使用Kimi的API接口,kimi-free-api真香
今年AI应用兴起,各类智能体涌现,但API免费额度有限。为解决这一问题,GitHub上的[kimi-free-api](https://github.com/LLM-Red-Team/kimi-free-api)项目提供了方便,支持高速流式输出、多轮对话等,与ChatGPT接口兼容。此外,还有其他大模型的免费API转换项目,如跃问StepChat、阿里通义Qwen等。该项目可帮助用户免费体验,通过Docker-compose轻松部署。只需获取refresh_token,即可开始使用。这个开源项目促进了AI学习和开发,为探索AI潜力提供了新途径。
173 2