3.1_26 JavaSE-JDK8新特性 P2 【Stream流-操作集合】

相关链接


目录

内容来自 三更草堂 - java8函数式编程


数据准备


通过案例,使用Stream流对模拟数据进行一系列处理,熟悉Stream各常用方法

1. pom.xml 配置文件  参考笔记 -> 六、lombok插件

    <dependencies>
       <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>

2. AuthorReq 实体类

package com.groupies.jdk8.day02.common.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/20
 * @introduction 作者   
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期去重使用
public class AuthorReq {
    private Long id;
    //姓名
    private String name;
    //英文名
    private String engName;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //作品
    private List<BookReq> books;
}

3. BookReq 实体类

package com.groupies.jdk8.day02.common.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * @author GroupiesM
 * @date 2021/12/20
 * @introduction 书
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期去重使用
public class BookReq {
    private Long id;
    //书名
    private String name;
    //分类(多个分类用,分割)
    private String category;
    //评分
    private Integer score;
    //简介
    private String intro;
}

4. StreamService 服务类,提供初始化数据方法

package com.groupies.jdk8.day02.service;
import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/20
 * @introduction Stream流
 */
@Service
public class StreamService {
    /**
     * @return 初始化数据
     */
    public static List<AuthorReq> getAuthors() {
        //作者列表
        AuthorReq authorReq1 = new AuthorReq(1L, "蒙多", "Mondor", 33, "祖安人", null);
        AuthorReq authorReq2 = new AuthorReq(2L, "亚索", "Yasuo", 15, "托儿所", null);
        AuthorReq authorReq3 = new AuthorReq(3L, "易", "MasterYi", 14, "剑圣", null);
        AuthorReq authorReq4 = new AuthorReq(3L, "易", "", 0, "", null);
        AuthorReq authorReq5 = new AuthorReq(3L, "易大师", "MasterYi", 14, "剑圣", null);
        AuthorReq authorReq6 = new AuthorReq(2L, "亚索", "Yasuo", 15, "托儿所", null);

        //书籍列表
        List<BookReq> books1 = new ArrayList<>();
        List<BookReq> books2 = new ArrayList<>();
        List<BookReq> books3 = new ArrayList<>();

        books1.add(new BookReq(1L, "刀的两侧是光明与黑暗", "哲学,爱情", 88, "用一把刀划分了爱恨"));
        books1.add(new BookReq(2L, "一个人不能死在同一把刀下", "个人成长,爱情", 99, "讲述如何从失败中明悟真理"));

        books2.add(new BookReq(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new BookReq(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new BookReq(4L, "吹或不吹", "爱情,个人传记", 56, "一个哲学家的恋爱观注定很难把他所在的时代理解"));

        books3.add(new BookReq(5L, "你的剑就是我的剑", "爱情", 56, "无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new BookReq(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
        books3.add(new BookReq(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));

        authorReq1.setBooks(books1);
        authorReq2.setBooks(books2);
        authorReq3.setBooks(books3);
        authorReq4.setBooks(books3);
        authorReq5.setBooks(books3);
        authorReq6.setBooks(books2);

        ArrayList<AuthorReq> list = new ArrayList<>();
        list.add(authorReq1);
        list.add(authorReq2);
        list.add(authorReq3);
        list.add(authorReq4);
        list.add(authorReq5);
        list.add(authorReq6);
        return list;
    }
}

1. 简介


1.1 Stream流


a.简介:
  1.stream流思想:类似于工厂的生产流水线,通过多个工序对原材料进行加工,形成商品;
  2.stream功能:首先复制一份源数据(集合、数组等),再对复制的数据进行一系列流水线式的中间操作(这里的操作并不会影响源数据),返回一个持有结果的新Stream;

串行流 stream
并行流 parallelStream
Stream.of 静态方法
Arrays.stream静态方法
Stream.Iterate静态方法
Stream.generate静态方法
fileter过滤 / limit取前n / skip跳过前n
map映射 /concat合并 / sorted排序
distinct去重 / flatMap将流展开
foreach 逐一处理
count 统计个数
max&min 最大/小值
collect 数据集合
find 查找
match 匹配
reduce 聚合
Collection接口的实现类
可以使用
List
Set
....
创建Stream流
Map
arr数组
....
arr数组
无限流
迭代
生成
中间操作
终止操作

  3. Stream常用方法 (T表示泛型)

id方法名方法作用请求参数返回值类型操作类型
1创建流的六种方式创建
1.1stream所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream串行流-Stream<E>创建
1.2parallelStream所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream并行流-Stream<E>创建
1.3Arrays.stream静态方法,根据数组元素创建对应Stream流T[ ]Stream<T>创建
1.4Stream.of静态方法,根据任意元素创建对应Stream流
T…:请求参数属于可变长参数,详情查看 => 博客
TStream<T>创建
1.5Stream.iterate静态方法,创建无限流(迭代)
seed: 初始值
f : function->对初始值的迭代函数
T seed,
UnaryOperator<T> f
Stream<T>创建
1.6Stream.generate静态方法,创建无限流(生成)
Supplier属于系统内置函数式接口 -> 供给型接口
Supplier<T>Stream<T>创建
2filter过滤,符合条件的才会继续保留在Stream流中Predicate<T>Stream中间
3limit取用前几个longStream<T>中间
4skip跳过前几个longStream<T>中间
5map转换
将Stream流中的元素转为任意类型
Stream<T> -> Stream<R>
Function<T,R>Stream<R>中间
6concat静态方法,合并2个Stream流成为一个Stream流Stream<T>,
Stream<T>
Stream<T>中间
7sorted排序-Stream<T>中间
8distinct去重
distinct依赖Object的equals方法来判断是否是相同对象,如果需按照某元素去重时,注意重新定义equals方法
-Stream<T>中间
9flatMap将流展开Function<T,R>Stream<R>中间
10forEach逐一处理Consumer<T>void终止
11count统计个数-long终止操作
12collect参数 supplier :生成目标类型实例的方法,代表着目标容器是什么;
参数 accumulator :将操作的目标数据填充到supplier 生成的目标类型实例中去的方法,代表着如何将元素添加到容器中;
参数 combiner :将多个supplier 生成的实例整合到一起的方法,代表着规约操作,将多个结果合并。

假如我们需要将保存有多个Person实例的list做一个转变,变为以id为key,value为person的Map的话,那就可以使用这个方法了:
Map<Long,Person> personMap = list.stream()
 .collect(Maps::newHashMap,
    (map,p)>map.put(p.getId(),p),
    Map::putAll);


或:
Map<Long,Person> personMap = list.stream()
 .collect(() -> new HashMap<>(),
     (map ,p) ->map.put(p.getId(),p),
     (m ,n) -> m.putAll(n));
Supplier<R>supplier,
BiConsumer<R,T>accumulator,
BiConsumer<R,R>combiner
R终止
collect使用 Collectors(java.util.stream.Collectors)来进行各种 reduction 操作Collector<T, A, R>R终止
13max和min最大(小)值
Comparator<T> :比较器,指定比较大小的方式
Optional<T> :返回泛型T,包装一层Optional判空,这样就可以允许返回空值
Comparator<T>Optional<T>终止
14find查找
1. findFirst:如果一个集合数据是有序的,而且你要查找符合条件的第一条数据。这时用findFirst是比较合适的。
2. findAny:返回任意一条数据,不关心顺序。该方法不能保证获取的一定是流中的第一条数据。且在并行流中,findAny限制更少。
-Optional<T>终止
15match匹配
1. allMatch 全部匹配,则返回true
2. anymatch 任一一条匹配,则返回true
3. nonematch 全部不匹配,则返回true
Predicate<T>boolean终止
16reduce(accumulator)聚合
聚合的含义就是将多个值经过特定计算之后得到单个值
参数1. accumulator 计算方式
BinaryOperator<T>accumulatorOptional<T>终止
reduce(accumulator,
combiner)
accumulator 计算方式
combiner 初始值
T identity,
BinaryOperator<T>accumulator
T终止
reduce(identity,
accumulator,combiner)
详情查看=>博客
identity 初始值
accumulator 计算方式
combiner 并行计算下 合并各个线程的计算结果
U identity,
BiFunction<U,T,U>accumulator
BinaryOperator<U>combiner
Optional<U>终止
map + reduce组合map:实现数据类型的转换,符合reduce对数据的要求
reduce:实现数据的处理
longOptional<T>终止
  终止操作 返回值不再是Stream类型,后续不再支持链式调用
  非终止操作 返回值再是Stream类型,后续支持链式调用

  4. 创建Stream的两种方式:串行流stream() & 并行流parallelStream()
  串行流 适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
  并行流 适合没有线程安全问题、较单纯的数据处理任务。
  5. Stream流如果不调用终结方法,非终结方法中的内容是不会执行的

	//举例:这里的filter方法中的内容不会打印
	ArrayList<String> list = new ArrayList<>();
	list.stream().filter(ele -> {
	    System.out.println("正在执行filter方法");
	    return ele.startsWith("a");
	});

  6. Stream流一旦调用终结方法,就不能再使用了

	//举例:流调用了一次forEach(终结)方法,就不能再次调用了
    //数据准备
    Stream<Integer> stream = Stream.of(1, 2, 3, 4);
    //34
    stream.filter(e -> e>2).forEach(System.out::print);
    //流已被操作或关闭
    //java.lang.IllegalStateException: stream has already been operated upon or closed
    stream.forEach(System.out::println);
List<Book> collect = authors.stream()
    .distinct()
    .filter(author -> author.getAge() < 18)
    .map(author -> author.getBooks())
    .flatMap(Collection::stream)
    .filter(book -> book.getScore() > 70)
    .distinct()
    .collect(Collectors.toList());
System.out.println(collect);

b.注意事项:
  1.惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
  2.流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
  3.不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的)

c.为什么学:
  1.能够看懂公司里的代码 2.大数量下处理集合效率高 3.代码可读性高 4.消灭嵌套地狱

//查询未成年作家的评分在70以上的书籍 由于洋流影响所以作家和书籍可能出现重复,需要进行去重
List<Book> bookList = new ArrayList<>();
Set<Book> uniqueBookValues = new HashSet<>();
Set<Author> uniqueAuthorValues = new HashSet<>();
for (Author author : authors) {
    if (uniqueAuthorValues.add(author)) {
        if (author.getAge() < 18) {
            List<Book> books = author.getBooks();
            for (Book book : books) {
                if (book.getScore() > 70) {
                    if (uniqueBookValues.add(book)) {
                        bookList.add(book);
                    }
                }
            }
        }
    }
}
System.out.println(bookList);

1.2 系统内置函数式接口


来自JavaSE-JDK8新特性 P1 【Lambda表达式】

系统内置函数式接口

id函数式接口参数类型返回类型包含方法
⭐️ 1Predicate<T>
断言型接口
Tbooleanboolean test(T t)
⭐️ 2Consumer<T>
消费型接口
Tvoid void accept(T t)
⭐️ 3Function<T,R>
函数型接口
TRR apply(T t)
⭐️ 4Supplier<T>
供给型接口
TT get()

一、Predicate扩展接口

id函数式接口参数类型返回类型包含方法
1.1BiPredicate<T,U>T,Ubooleanboolean test(T t, U u)
1.2IntPredicateintbooleanboolean test(int value)
1.3LongPredicatelongbooleanboolean test(long value)
1.4DoublePredicatedoublebooleanboolean test(double value)

二、Consumer扩展接口

id函数式接口参数类型返回类型包含方法
2.1BiConsumer<T,U,R>T,Uvoidvoid accept(T t, U u)
2.2IntConsumerintvoid void accept(int value)
2.3LongConsumerlongvoid void accept(long value)
2.4DoubleConsumerdoublevoid void accept(double value)

三、Function扩展接口

id函数式接口参数
类型
返回
类型
包含方法
3.1BiFunction<T,U,R>T,URR apply(T t, U u)
3.2IntFunction<R>intRR apply(int value)
3.3LongFunction<R>longRR apply(long value)
3.4DoubleFunction<R>doubleRR apply(double value)
3.5IntToLongFunctionintlonglong applyAsLong(int value)
3.6IntToDoubleFunctionintdoubledouble applyAsDouble(int value)
3.7LongToIntFunctionlongintint applyAsInt(long value)
3.8LongToDoubleFunctionlongdoubledouble applyAsDouble(long value)
3.9DoubleToIntFunctiondoubleintint applyAsInt(double value)
3.10DoubleToLongFunctiondoublelonglong applyAsLong(double value)
3.11BinaryOperator<T>
extens
BiFunction<T,U,R>
T,TTminBy
在这里插入图片描述
maxBy
在这里插入图片描述
3-.12UnaryOperator<T>
extends
Function<T, T>
TTstatic UnaryOperator identity()
{return t -> t;}

2. 创建流


[1 获取流]

id方法名方法作用请求参数返回值类型操作类型
1.1stream所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream串行流-Stream<E>创建
1.2parallelStream所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream并行流-Stream<E>创建
1.3Arrays.stream静态方法,根据数组元素创建对应Stream流T[ ]Stream<T>创建
1.4Stream.of静态方法,根据任意元素创建对应Stream流
T…:请求参数属于可变长参数,详情查看 => 博客
TStream<T>创建
1.5Stream.iterate静态方法,创建无限流(迭代)
seed: 初始值
f : function->对初始值的迭代函数
T seed,
UnaryOperator<T> f
Stream<T>创建
1.6Stream.generate静态方法,创建无限流(生成)
Supplier属于系统内置函数式接口 -> 供给型接口
Supplier<T>Stream<T>创建

案例1.1 stream (Collection子类可用-串行流)

案例代码一获取Stream流

package com.groupies.jdk8.day02.practice;

import java.util.*;
import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/21
 * @introduction 获取Stream流
 * 获取方式1: 通过Collection接口stream获取串行流
 */
public class Demo1CollectionStream {
    public static void main(String[] args) {
        //1 list -> stream
        ArrayList<String> list = new ArrayList<>();
        Stream<String> streamList = list.stream();

        //2 set -> stream
        HashSet<String> set = new HashSet<>();
        Stream<String> streamSet = set.stream();

        //3 vector -> stream
        Vector<String> vector = new Vector<>();
        Stream<String> streamVector = vector.stream();

        //4 map -> stream
        HashMap<String, Object> map = new HashMap<>();
        //4.1 map(key) -> stream
        Set<String> strings = map.keySet();
        Stream<String> streamMap1 = map.keySet().stream();//map.keySet() -> Set<String>
        //4.2 map(value) -> stream
        Collection<Object> values = map.values();
        Stream<Object> streamMap2 = map.values().stream();//map.values() -> Collection<Object>
  }
}

案例1.2 parallelStream(Collection子类可用-串行流)

案例代码二获取Stream流

package com.groupies.jdk8.day02.practice;

import java.util.*;
import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/21
 * @introduction 获取Stream流
 * 获取方式2: 通过Collection接口parallelStream获取并行流
 */
public class Demo2CollectionParallelStream {
    public static void main(String[] args) {
        //1 list -> stream
        ArrayList<String> list = new ArrayList<>();
        Stream<String> streamList = list.parallelStream();

        //2 set -> stream
        HashSet<String> set = new HashSet<>();
        Stream<String> streamSet = set.parallelStream();

        //3 vector -> stream
        Vector<String> vector = new Vector<>();
        Stream<String> streamVector = vector.parallelStream();

        //4 map -> stream
        HashMap<String, Object> map = new HashMap<>();
        Set<String> strings = map.keySet();
        Stream<String> streamMap1 = map.keySet().parallelStream();//map.keySet() -> Set<String>

        Collection<Object> values = map.values();
        Stream<Object> streamMap2 = map.values().parallelStream();//map.values() -> Collection<Object>
    }
}

案例1.3 Arrays.stream 静态方法(数组)

案例代码三获取Stream流

package com.groupies.jdk8.day02.practice;

import java.util.Arrays;
import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/21
 * @introduction 获取Stream流
 * 获取方式3:Arrays.stream 静态方法,根据数组元素创建对应Stream流
 */
public class Demo3ArraysStream {
    public static void main(String[] args) {
        String[] arr = {"aa", "bb", "cc"};
        Stream<String> streamArr = Arrays.stream(arr);
        streamArr.forEach(System.out::print);//aabbcc
    }
}

案例1.4 Stream.of 静态方法(任意)

案例代码四获取Stream流

package com.groupies.jdk8.day02.practice;

import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/21
 * @introduction 获取Stream流
 * 获取方式4:Stream.of 静态方法,根据任意元素创建对应Stream流
 */
public class Demo4StreamOf {
    public static void main(String[] args) {
        //1 引用类型数组(String)
        String[] arr1 = {"aa", "bb"};
        Stream<String> stream1 = Stream.of(arr1);
        //2 引用类型数组(Object)
        Stream<Object> stream2 = Stream.of(1, "a");
        /*
            1
            a
         */
        stream2.forEach(System.out::println);

        //2 基本类型数组(int) 直接打印得到的是地址值
        int[] arr3 = {1, 2};
        //[I@31221be2
        Stream.of(arr3).forEach(System.out::println);
    }
}

案例1.5 Stream.iterate 静态方法(无限流-迭代)

案例代码五获取Stream流

package com.groupies.jdk8.day02.practice;

import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/21
 * @introduction 获取Stream流
 * 获取方式5: 静态方法,提供一个初始值,和迭代函数,生成 Stream 无限流
 */
public class Demo5StreamIterate {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.iterate(0, x -> x + 1);
        //limit取前四个 -> filter过滤只保留偶数
        stream.limit(4).filter(x -> x % 2 == 0).forEach(System.out::print);//02
    }
}

案例1.6 Stream.generate 静态方法(无限流-生成)

案例代码六获取Stream流

package com.groupies.jdk8.day02.practice;

import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/21
 * @introduction 获取Stream流
 * 获取方式6: 静态方法,提供一个Supplier接口的实现方法, 生成 Stream 无限流
 */
public class Demo6StreamGenerate {
    public static void main(String[] args) {
        Stream<Double> stream = Stream.generate(Math::random);
        //limit取前三个
        stream.limit(3).forEach(System.out::println);
    }
}

3.中间操作


[2 filter 过滤]

id方法名方法作用请求参数返回值类型操作类型
2filter过滤,符合条件的才会继续保留在Stream流中Predicate<T>Stream中间

案例2.1 按属性取值范围

需求: 过滤姓名长度大于3的作家,并打印

处理前:

1...蒙多
2...亚索
3...3...3...易大师
2...亚索

处理后:

1...蒙多
2...亚索
3...3...2...亚索

案例代码七filter 按属性取值范围

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作filter
 */
public class Demo7StreamFilter {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        authors.stream()
                .filter(distinctByKey(author -> author.getId() + author.getName()))
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName()));
    }

    //此方法来源于:https://blog.csdn.net/haiyoung/article/details/80934467
    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}

案例2.2 去重

需求: id和姓名都相同的视作重复,过滤重复值,并打印

处理前:

1...蒙多
2...亚索
3...3...3...易大师
2...亚索

处理后:

1...蒙多
2...亚索
3...3...易大师

案例代码八filter 去重

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作filter
 */
public class Demo8StreamFilter {
    @Autowired
    private StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = StreamService.getAuthors();
        authors.stream()
                .filter(distinctByKey(author -> author.getId() + author.getName()))
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName()));
    }

    //此方法来源于:https://blog.csdn.net/haiyoung/article/details/80934467
    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}

[3 limit 前n]


案例3.1 打印前n个

id方法名方法作用请求参数返回值类型操作类型
3limit取用前几个longStream<T>中间

需求: 打印前1个作家姓名(默认排序)

处理前:

1...蒙多
2...亚索
3...3...3...易大师
2...亚索

处理后:

1...蒙多

案例代码九limit 前n

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作limit
 */
public class Demo9StreamLimit {
    @Autowired
    private static StreamService streamService;
    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.stream().forEach(author -> System.out.println(author.getId() + "..." + author.getName()));
        System.out.println("---");
        authors.stream()
                .limit(1)
                .forEach(author -> System.out.println(author.getName()));
    }
}

[4 skip 跳过前n]


案例4.1 跳过前n个,再打印

id方法名方法作用请求参数返回值类型操作类型
4skip跳过前几个longStream<T>中间

需求: 打印除了前4个作者以外的作者姓名(默认排序)

处理前:

1...蒙多
2...亚索
3...3...3...易大师
2...亚索

处理后:

3...易大师
2...亚索

案例代码十skip 跳过前n

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作skip
 */
public class Demo10StreamSkip {
    @Autowired
    private static StreamService streamService;
    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        authors.stream()
                .skip(4)
                .forEach(author -> System.out.println(author.getName()));
    }
}

[5 map 转换]

id方法名方法作用请求参数返回值类型操作类型
5map转换
将Stream流中的元素转为任意类型
Stream<T> -> Stream<R>
Function<T,R>Stream<R>中间

案例5.1 作者英文名String

需求: 获取所有不为空的作者小写英文名,并打印

处理前:

Mondor
Yasuo
MasterYi

MasterYi
Yasuo

处理后:

{mondor,yasuo,masteryi,masteryi,yasuo}

案例代码十一map  作者英文名String

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作map
 */
public class Demo11StreamMap {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.stream().map(AuthorReq::getEngName).forEach(System.out::println);
        String bookNames = authors.stream()//Stream<AuthorReq>
                .map(AuthorReq::getEngName)//Stream<String>
                .map(String::toLowerCase)
                .filter(s -> s.length() > 0)
                .collect(Collectors.joining(",", "{", "}"));
        System.out.println(bookNames);
    }
}

案例5.2 List[BookName]

需求: 获取所有书名集合(去重),并打印

处理前:

BookReq(id=1, name=刀的两侧是光明与黑暗, category=哲学,爱情, score=88, intro=用一把刀划分了爱恨)
BookReq(id=2, name=一个人不能死在同一把刀下, category=个人成长,爱情, score=99, intro=讲述如何从失败中明悟真理)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)

处理后:

刀的两侧是光明与黑暗
一个人不能死在同一把刀下
那风吹不到的地方
吹或不吹
你的剑就是我的剑
风与剑

案例代码十二map  List<Book>

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作map
 */
public class Demo12StreamMap {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.stream().map(AuthorReq::getBooks).flatMap(book->book.stream()).forEach(System.out::println);
        List<String> bookNameList = authors.stream()//Stream<AuthorReq>
                .map(AuthorReq::getBooks)//Stream<List<BookReq>>
                .flatMap(books -> books.stream())//Stream<BookReq> 也可以写作 flatMap(List::stream)
                .map(BookReq::getName)//Stream<String>
                .distinct()
                .collect(Collectors.toList());

        bookNameList.forEach(System.out::println);
    }
}

案例5.3 年龄求和

需求: 对所有作者年龄求和,并打印

处理前:

age:33
age:15
age:14
age:0
age:14
age:15

处理后:

sum:91

案例代码十三map  年龄求和

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Optional;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作map
 */
public class Demo13StreamMap {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.forEach(author -> System.out.println("age:" + author.getAge()));

        Optional<Integer> reduce = authors.stream()
                .map(AuthorReq::getAge)
                .reduce(Integer::sum);
        System.out.println("sum:" + reduce.get());
    }
}

[6 concat 组合]


案例6.1 合并两个流

id方法名方法作用请求参数返回值类型操作类型
6concat静态方法,合并Stream<T>,
Stream<T>
Stream<T>中间

需求:前1个作者,除了前4个,剩下的作者,合并成为新的list,并打印

处理前:

1...蒙多//前1个
2...亚索
3...3...3...易大师//除了前4个,剩下的
2...亚索//除了前4个,剩下的

处理后:

1...蒙多
3...易大师
2...亚索

案例代码十四concat  合并

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作concat
 */
public class Demo14StreamConcat {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.forEach(author -> System.out.println(author.getId() + "..." + author.getName()));
        System.out.println("---");
        //1.取前1个作者(案例3)
        Stream<AuthorReq> limit = authors.stream().limit(1);
        //2.取除了前3个作者以外的作者(案例4)
        Stream<AuthorReq> skip = authors.stream().skip(4);
        //3.合并前两者到一个流中,并形成新的list
        List<AuthorReq> authorList = Stream
                .concat(limit, skip)
                .collect(Collectors.toList());
        authorList.forEach(author -> System.out.println(author.getId() + "..." + author.getName()));
    }
}

[7 sorted 排序]


案例7.1 年龄升序

id方法名方法作用请求参数返回值类型操作类型
7sorted排序-Stream<T>中间

需求: 1.年龄降序,并打印
    2.年龄升序,并打印

注意: 1.如果调用空参的sorted()方法,需要流中的元素是实现了Comparable。
    2.实现排序有多重语法和方式,具体方式可以参考JavaSE-JDK8新特性 P1 【Lambda表达式】中案例2

处理前:

1...蒙多...33
2...亚索...15
3......14
3......0
3...易大师...14
2...亚索...15

处理后(年龄降序):

1...蒙多...33
2...亚索...15
2...亚索...15
3......14
3...易大师...14
3......0

处理后(年龄升序):

3......0
3......14
3...易大师...14
2...亚索...15
2...亚索...15
1...蒙多...33

如果调用空参的sorted()方法,需要流中的元素是实现了Comparable

package com.groupies.jdk8.day02.common.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

/**
* @author GroupiesM
* @date 2021/12/20
* @introduction 作者
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期去重使用
public class AuthorReq implements Comparable<AuthorReq> {
   private Long id;
   //姓名
   private String name;
   //英文名
   private String engName;
   //年龄
   private Integer age;
   //简介
   private String intro;
   //作品
   private List<BookReq> books;

   //自定义排序,按照年龄降序
   @Override
   public int compareTo(AuthorReq o) {
       return this.getAge().compareTo(o.getAge());
   }
}

案例代码十五sorted  排序

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 中间操作sorted
 */
public class Demo15StreamSorted {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
		//处理前
        authors.stream().forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getAge()));
        System.out.println("...");

        //1.年龄降序
        authors.stream()
                //.sorted(((o1, o2) -> o2.getAge()-o1.getAge()))//方式一
                .sorted(((o1, o2) -> o2.getAge().compareTo(o1.getAge())))//方式二
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getAge()));
        System.out.println("...");

        //2.年龄升序
        authors.stream()
                //.sorted(((o1, o2) -> o1.getAge()-o2.getAge()))//方式一
                //.sorted(((o1, o2) -> o1.getAge().compareTo(o2.getAge())))//方式二
                //.sorted((Comparator.comparingInt(AuthorReq::getAge)))//方式三
                .sorted()//方式四:AuthorReq类继承了Comparable,并实现了compareto方法
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getAge()));
        System.out.println("...");
    }
}

[8 distinct 去重]

id方法名方法作用请求参数返回值类型操作类型
8distinct去重
distinct依赖Object的equals方法来判断是否是相同对象,如果需按照某元素去重时,注意重新定义equals方法
-Stream<T>中间

实现方式: distinct基于流中对象的hashCode()和equals()工作的。案例8.1、案例8.2中是通过使用lombok的@EqualsAndHashCode实现hashCode和equals的。


⭐️ @EqualsAndHashCode

1. 此注解会生成equals(Object other) 和 hashCode()方法。
2. 它默认使用非静态,非瞬态的属性
3. 可通过参数exclude排除一些属性
4. 可通过参数of指定仅使用哪些属性
  指定单个属性  (of = “id”)
  指定多个属性  (of = {“id”,“name”})
5. 它默认仅使用该类中定义的属性且不调用父类的方法
6. 可通过callSuper=true解决上一点问题。让其生成的方法中调用父类的方法。
7. @Data相当于@Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode这5个注解的合集。


案例8.1 @注解 按整个对象去重

需求: 通过使用@EqualsAndHashCode注解,按整个对象去重
处理前:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣
3.........0...
3...易大师...MasterYi...14...剑圣
2...亚索...Yasuo...15...托儿所

处理后:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣
3.........0...
3...易大师...MasterYi...14...剑圣

@EqualsAndHashCode 不指定任何参数,表示按照整个对象去重(所有属性全等则认为相等)

package com.groupies.jdk8.day02.common.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

/**
* @author GroupiesM
* @date 2021/12/20
* @introduction 作者
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class AuthorReq {
   private Long id;
   //姓名
   private String name;
   //英文名
   private String engName;
   //年龄
   private Integer age;
   //简介
   private String intro;
   //作品
   private List<BookReq> books;
}

案例代码十六distinct  @注解 整个对象去重

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 中间操作distinct
 */
public class Demo16StreamDistinct {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getEngName() + "..." + author.getAge() + "..." + author.getIntro()));
        System.out.println("...");

        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getEngName() + "..." + author.getAge() + "..." + author.getIntro()));
    }
}

案例8.2 @注解 按id,name去重

需求: 通过使用@EqualsAndHashCode注解,按整个对象去重
处理前:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣
3.........0...
3...易大师...MasterYi...14...剑圣
2...亚索...Yasuo...15...托儿所

处理后:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣
3...易大师...MasterYi...14...剑圣

@EqualsAndHashCode 指定id和name,表示id和name都一样时,表示两个对象相等

package com.groupies.jdk8.day02.common.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

/**
* @author GroupiesM
* @date 2021/12/20
* @introduction 作者
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class AuthorReq {
   private Long id;
   //姓名
   private String name;
   //英文名
   private String engName;
   //年龄
   private Integer age;
   //简介
   private String intro;
   //作品
   private List<BookReq> books;
}

案例代码十七distinct  @注解 按id,name去重

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 中间操作distinct
 *  1.通过使用@EqualsAndHashCode注解,按id,name去重
 * @EqualsAndHashCode(of = {"id","name"})
 */
public class Demo17StreamDistinct {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getEngName() + "..." + author.getAge() + "..." + author.getIntro()));
        System.out.println("...");

        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getEngName() + "..." + author.getAge() + "..." + author.getIntro()));
    }
}


案例8.3 filter自定义方法按指定属性去重 = 2.2

需求: id和姓名都相同的视作重复,过滤重复值,并打印

引用案例2.2 案例代码八filter 去重

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作filter
 * 需求:id和姓名都相同的视作重复,过滤重复值,并打印
 */
public class Demo8StreamFilter {
    @Autowired
    private StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = StreamService.getAuthors();
        authors.stream()
                .filter(distinctByKey(author -> author.getId() + author.getName()))
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName()));
    }

    //此方法来源于:https://blog.csdn.net/haiyoung/article/details/80934467
    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}

案例8.4 collect转TreeSet按单属性去重

需求: TreeSet实现单属性排序
注意: 这里使用了TreeSet的2个特性(排序、去重)中的去重特性
    参考 JavaSE-JDK8新特性 P1 【Lambda表达式】 案例2.8 - TreeSet - freeStyle(多条件排序)

处理前:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣
3.........0...
3...易大师...MasterYi...14...剑圣
2...亚索...Yasuo...15...托儿所

处理后:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣

案例代码十八distinct  collect转TreeSet按单属性整去重

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 中间操作distinct扩展
 */
public class Demo18StreamDistinct {
    @Autowired
    private StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = StreamService.getAuthors();
        authors.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> 
                		new TreeSet<>(Comparator.comparing(AuthorReq::getId)))
                        , ArrayList::new))
                .forEach(System.out::println);
    }
}

案例8.5 collect转TreeSet按多属性去重

需求: TreeSet实现多属性排序
注意: 这里使用了TreeSet的2个特性(排序、去重)中的去重特性
    参考 JavaSE-JDK8新特性 P1 【Lambda表达式】 案例2.8 - TreeSet - freeStyle(多条件排序)

处理前:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣
3.........0...
3...易大师...MasterYi...14...剑圣
2...亚索...Yasuo...15...托儿所

处理后:

1...蒙多...Mondor...33...祖安人
2...亚索...Yasuo...15...托儿所
3......MasterYi...14...剑圣
3...易大师...MasterYi...14...剑圣

案例代码十九distinct  collect转TreeSet按多属性整去重

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 中间操作distinct扩展
 * @EqualsAndHashCode(of = {"id","name"})
 */
public class Demo19StreamDistinct {
    @Autowired
    private StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = StreamService.getAuthors();
        //处理前
        authors.forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getEngName() + "..." + author.getAge() + "..." + author.getIntro()));
        System.out.println("...");

        authors.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> 
                		new TreeSet<>((o1, o2) -> {
                            int idAsc = o1.getId().compareTo(o2.getId());//id升序
                            int nameAsc = o1.getName().compareTo(o2.getName());//name升序
                            if (idAsc == 0 && nameAsc == 0) return 0;//如果name和id都相等,则去重
                            return 1;//默认返回1,表示不排序
                        }))
                        , ArrayList::new))
                .forEach(author -> System.out.println(author.getId() + "..." + author.getName() + "..." + author.getEngName() + "..." + author.getAge() + "..." + author.getIntro()));
    }
}

[9 flatMap 展开流]


id方法名方法作用请求参数返回值类型操作类型
9flatMap将流展开Function<T,R>Stream<R>中间

案例9.1 List[BookName] = 5.2

需求: 获取所有书名集合(去重),并打印

引用案例5.2 案例代码十二map  List<Book>

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2021/12/24
 * @introduction 中间操作map
 * 需求:获取所有书名集合(去重),并打印
 */
public class Demo12StreamMap {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.stream().map(AuthorReq::getBooks).flatMap(book->book.stream()).forEach(System.out::println);
        List<String> bookNameList = authors.stream()//Stream<AuthorReq>
                .map(AuthorReq::getBooks)//Stream<List<BookReq>>
                .flatMap(books -> books.stream())//Stream<BookReq>  也可以写作 flatMap(List::stream)
                .map(BookReq::getName)//Stream<String>
                .distinct()
                .collect(Collectors.toList());

        bookNameList.forEach(System.out::println);
    }
}

⭐️ 案例9.2 书籍分类

需求: 打印现有书籍分类。要求对分类进行去重。不能出现这种格式:哲学,爱情

处理前:

BookReq(id=1, name=刀的两侧是光明与黑暗, category=哲学,爱情, score=88, intro=用一把刀划分了爱恨)
BookReq(id=2, name=一个人不能死在同一把刀下, category=个人成长,爱情, score=99, intro=讲述如何从失败中明悟真理)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)

处理后:

哲学
爱情
个人成长
个人传记

案例代码二十flatMap  所有书籍分类

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Stream;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 中间操作distinct
 */
public class Demo20StreamFlatMap {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.stream().map(AuthorReq::getBooks).flatMap(List::stream).forEach(System.out::println);
        System.out.println("...");

        authors.stream()//Stream<AuthorReq>
                .flatMap(author -> author.getBooks().stream())//Stream<BookReq>
                .map(BookReq::getCategory)//Stream<String> :这里的分类有可能是多个,例如a,b
                .map(category->category.split(","))//Stream<String[]> :将多个分类的情况按分隔符进行拆分,形成数组
                .flatMap(Stream::of)//Stream<String> :再将流展开,新的Stream里面只有书籍分类
                .distinct()//根据String类的equals方法进行去重
                .forEach(System.out::println);
    }
}

4.终结操作

[10 forEach 遍历]


案例10.1 遍历打印

id方法名方法作用请求参数返回值类型操作类型
10forEach逐一处理Consumer<T>void终止

需求: 打印所有作家名字

执行结果:

蒙多
亚索
易
易
易大师
亚索
...
蒙多
亚索
易
易
易大师
亚索

案例代码二十一forEach  打印所有作家名字

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 终结操作foreach
 */
public class Demo21StreamForeach {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        authors.stream().map(AuthorReq::getName).forEach(System.out::println);//方式一
        System.out.println("...");
        authors.stream().forEach(author -> System.out.println(author.getName()));//方式二
    }
}

[11 count 个数]


案例11.1 书籍总数

id方法名方法作用请求参数返回值类型操作类型
11count统计个数-long终止操作

需求: 统计所有作者的所有书籍总数,并打印

执行结果:

17

案例代码二十二count  统计书籍数量

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 终结操作count
 */
public class Demo22StreamCount {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        long count = authors.stream()
                .map(AuthorReq::getBooks)
                .flatMap(List::stream)
                .count();
        System.out.println(count);
    }
}

[12 collect 转集合]

id方法名方法作用请求参数返回值类型操作类型
12collect参数 supplier :生成目标类型实例的方法,代表着目标容器是什么;
参数 accumulator :将操作的目标数据填充到supplier 生成的目标类型实例中去的方法,代表着如何将元素添加到容器中;
参数 combiner :将多个supplier 生成的实例整合到一起的方法,代表着规约操作,将多个结果合并。

假如我们需要将保存有多个Person实例的list做一个转变,变为以id为key,value为person的Map的话,那就可以使用这个方法了:
Map<Long,Person> personMap = list.stream()
 .collect(Maps::newHashMap,
    (map,p)>map.put(p.getId(),p),
    Map::putAll);


或:
Map<Long,Person> personMap = list.stream()
 .collect(() -> new HashMap<>(),
     (map ,p) ->map.put(p.getId(),p),
     (m ,n) -> m.putAll(n));
Supplier<R>supplier,
BiConsumer<R,T>accumulator,
BiConsumer<R,R>combiner
R终止
collect使用 Collectors(java.util.stream.Collectors)来进行各种 reduction 操作Collector<T, A, R>R终止

:这里案例只展示Collectors部分用法,其他用法参考 Java8中Collectors详解


案例12.1_1 Collectors.joining 转字符串

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| joining | 返回一个Collector收集器,它按遇见顺序将输入元素连接成String。 | - | Collector<CharSequence,?,String> |
需求: 拼接字符串,内容为所有作者姓名。

执行结果:

蒙多亚索易易易大师亚索

案例代码二十三Collectors.joining

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.joining())
 */
public class Demo23StreamCollectorsJoining {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        String authorName = authors.stream().map(AuthorReq::getName)
                .collect(Collectors.joining());
        System.out.println(authorName);
    }
}

案例12.1_2 Collectors.joining(delimiter)

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| joining(delimiter) | 返回一个Collector收集器,它按遇见顺序将输入元素连接成String。 | - | Collector<CharSequence,?,String> |

需求: 拼接字符串,内容为所有作者姓名。满足格式 a🐳b🐳c

执行结果:

蒙多🐳亚索🐳易🐳易🐳易大师🐳亚索

案例代码二十四Collectors.joining(delimiter)

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.joining(delimiter))
 */
public class Demo24StreamCollectorsJoining {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        String authorName = authors.stream().map(AuthorReq::getName)
                .collect(Collectors.joining("🐳"));
        System.out.println(authorName);
    }
}

案例12.1_3 Collectors.joining(delimiter, prefix, suffix)

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| joining(delimiter, prefix, suffix) | 返回一个Collector收集器,它以遇见顺序将由指定分隔符分隔的输入元素与指定的前缀和后缀连接起来。 | - | Collector<CharSequence,?,String> |
需求: 拼接字符串,内容为所有作者姓名。满足格式 🐘a,b,c🦧

执行结果:

🐘蒙多,亚索,,,易大师,亚索🦧

案例代码二十五Collectors.joining(delimiter, prefix, suffix)

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.joining(delimiter, prefix, suffix))
 * 需求:拼接字符串,内容为所有作者姓名。满足格式 [a,b,c]
 */
public class Demo25StreamCollectorsJoining {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        String authorName = authors.stream().map(AuthorReq::getName)
                .collect(Collectors.joining(",","🐘","🦧"));
        System.out.println(authorName);
    }
}

案例12.2 Collectors.toList 转List

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| toList | 返回一个Collector收集器,它将输入元素累积到一个新的List中。返回的List的类型,可变性,可序列化或线程安全性无法保证;如果需要更多地控制返回的List,请使用toCollection(Supplier)。 | - | Collector<T,?,List<T>> |

需求: 获取一个存放所有作者名字的List集合。

执行结果:

[蒙多, 亚索,,, 易大师, 亚索]

案例代码二十六Collectors.toList

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.toList)
 */
public class Demo26StreamCollectorsToList {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        List<String> nameList = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toList());

        System.out.println(nameList);
    }
}

案例12.3 Collectors.toSet 转Set

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| toSet | 返回一个Collector收集器,它将输入元素累积到一个新的Set中。返回的Set的类型,可变性,可序列化或线程安全性无法保证;如果需要更多地控制返回的Set,请使用toCollection(Supplier)。 | - | Collector<T,?,Set<T>> |

需求: 获取一个存放所有作者名字的Set集合。

执行结果:

BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=2, name=一个人不能死在同一把刀下, category=个人成长,爱情, score=99, intro=讲述如何从失败中明悟真理)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=1, name=刀的两侧是光明与黑暗, category=哲学,爱情, score=88, intro=用一把刀划分了爱恨)

案例代码二十七Collectors.toSet

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 终结操作collect(Collector.toSet)
 */
public class Demo27StreamCollectorsToSet {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Set<BookReq> bookSet = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());
        bookSet.forEach(System.out::println);
    }
}

案例12.4 Collectors.toMap 转Map

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| toMap | 返回一个Collector收集器,它将元素累积到Map中,其键和值是将提供的映射函数应用于输入元素的结果。 | - | Collector<T,?,Map<K,U>> |

需求: 获取一个Map集合,map的key为作者名,value为List<Book>。

执行结果:

蒙多 = 
BookReq(id=1, name=刀的两侧是光明与黑暗, category=哲学,爱情, score=88, intro=用一把刀划分了爱恨)
BookReq(id=2, name=一个人不能死在同一把刀下, category=个人成长,爱情, score=99, intro=讲述如何从失败中明悟真理)
亚索 = 
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)
BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)= 
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
易大师 = 
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)

案例代码二十八Collectors.toMap

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2021/12/27
 * @introduction 终结操作collect(Collector.toMap)
 */
public class Demo28StreamCollectorsToMap {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Map<String, List<BookReq>> authorMap = authors.stream()
                .distinct()
                .collect(Collectors.toMap(AuthorReq::getName, AuthorReq::getBooks));

        authorMap.keySet().forEach(key -> {
            System.out.println(key + " = ");
            List<BookReq> bookList = authorMap.get(key);
            bookList.stream().forEach(System.out::println);
        });
    }
}

案例12.5 Collectors.AveragingInt 平均值

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| AveragingDouble | 返回一个Collector收集器,它生成应用于输入元素的double值函数的算术平均值。如果没有元素,则结果为0。 | - | Collector<T,?,Double<K,U>> |

需求: 统计所有学生的平均总年龄

执行结果:

15.166666666666666

案例代码二十九Collectors.AveragingInt

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.AveragingInt)
 */
public class Demo29StreamCollectorsAveragingInt {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Double collect = authors.stream()
                .collect(Collectors.averagingInt(AuthorReq::getAge));
        System.out.println(collect);
    }
}

案例12.6 Collectors.CollectingAndThen 收集+转换

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| CollectingAndThen | 调整Collector收集器以执行其它的结束转换。 | - | Collector<T,A,RR> |

需求: 以指定格式输出: 年龄总和是 -> 输出所有作者的平均年龄。

执行结果:

年龄总和是:15.166666666666666

案例代码三十Collectors.CollectingAndThen

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.CollectingAndThen)
 */
public class Demo25StreamCollectorsCollectingAndThen {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        String result = authors.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.averagingInt(AuthorReq::getAge),
                        ele -> "年龄总和是:" + ele));
        System.out.println(result);
    }
}

案例12.7 Collectors.GroupingBy 聚合

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| GroupingBy | 返回一个Collector收集器对T类型的输入元素执行"group by"操作,根据分类函数对元素进行分组,并将结果返回到Map。 | - | Collector<T,?,Long> |

需求: 1.要求对分类进行去重。不能出现这种格式:哲学,爱情
    2.分类后再进行聚合,获取一个Map集合,map的key为书籍分类,value为书籍信息

执行结果:

刀的两侧是光明与黑暗=[哲学,爱情]
一个人不能死在同一把刀下=[个人成长,爱情]
那风吹不到的地方=[哲学]
那风吹不到的地方=[哲学]
吹或不吹=[爱情,个人传记]
你的剑就是我的剑=[爱情]
风与剑=[个人传记]
风与剑=[个人传记]
你的剑就是我的剑=[爱情]
风与剑=[个人传记]
风与剑=[个人传记]
你的剑就是我的剑=[爱情]
风与剑=[个人传记]
风与剑=[个人传记]
那风吹不到的地方=[哲学]
那风吹不到的地方=[哲学]
吹或不吹=[爱情,个人传记]
...
个人成长 = 
BookReq(id=2, name=一个人不能死在同一把刀下, category=个人成长, score=99, intro=讲述如何从失败中明悟真理)
个人传记 = 
BookReq(id=4, name=吹或不吹, category=个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)
BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)
爱情 = 
BookReq(id=1, name=刀的两侧是光明与黑暗, category=爱情, score=88, intro=用一把刀划分了爱恨)
BookReq(id=2, name=一个人不能死在同一把刀下, category=爱情, score=99, intro=讲述如何从失败中明悟真理)
BookReq(id=4, name=吹或不吹, category=爱情, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)
BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)
哲学 = 
BookReq(id=1, name=刀的两侧是光明与黑暗, category=哲学, score=88, intro=用一把刀划分了爱恨)
BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头)

案例代码三十一Collectors.GroupingBy

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.GroupingBy)
 */
public class Demo31StreamCollectorsGroupingBy {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //处理前
        authors.stream().map(AuthorReq::getBooks).flatMap(List::stream).forEach(book -> System.out.println(book.getName() + "=[" + book.getCategory() + "]"));
        System.out.println("...");

        Map<String, List<BookReq>> map = authors.stream()//Stream<AuthorReq>
                .map(AuthorReq::getBooks)//Stream<List<BookReq>>:
                .flatMap(List::stream)//Stream<BookReq>:展开流
                .map(bookReq -> {
                    List<BookReq> list = new ArrayList<>();
                    String[] cateArr = bookReq.getCategory().split(",");
                    for (String cate : cateArr) {
                        BookReq book = new BookReq();
                        BeanUtils.copyProperties(bookReq,book);
                        book.setCategory(cate);
                        list.add(book);
                    }
                    return list;
                })//Stream<List<BookReq>>:按分类拆分BookReq对象
                .flatMap(List::stream)//Stream<BookReq>:展开流
                .distinct()
                .collect(Collectors.groupingBy(BookReq::getCategory));

        map.keySet().forEach(key -> {
            System.out.println(key + " = ");
            List<BookReq> bookList = map.get(key);
            bookList.stream().forEach(System.out::println);
        });
    }
}

案例12.8 Collectors.mapping 映射

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| mapping | 通过在累积之前将映射函数应用于每个输入元素,将Collector收集器接受U类型的元素调整为一个接受T类型的元素。 | - | Collector<T,?,R> |
需求: 同案例12.1_3;拼接字符串,内容为所有作者姓名。满足格式 🐘a,b,c🦧

执行结果:

🐘蒙多,亚索,,,易大师,亚索🦧

案例代码三十二Collectors.mapping

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.Mapping())
 */
public class Demo32StreamCollectorsMapping {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        String authorName = authors.stream().collect(Collectors.mapping(AuthorReq::getName, Collectors.joining(",","🐘","🦧")));
        System.out.println(authorName);
    }
}

案例12.9 Collectors.maxBy 最大值

| 方法名 | 方法作用 | 请求参数 |返回值类型 |
|:–|:–|:–|:–|:–|:–|
| maxBy | 返回一个Collector收集器,它根据给定的Comparator比较器生成最大元素,描述为Optional。 | - | Collector<T,?,Optional<T>> |
| minBy | 返回一个Collector收集器,它根据给定的Comparator比较器生成最小元素,描述为Optional。 | - | Collector<T,?,Optional<T>> |

需求: 找出年龄最大的作者的信息

执行结果:

蒙多 = max=BookReq(id=2, name=一个人不能死在同一把刀下, category=个人成长,爱情, score=99, intro=讲述如何从失败中明悟真理), min=BookReq(id=1, name=刀的两侧是光明与黑暗, category=哲学,爱情, score=88, intro=用一把刀划分了爱恨)}
亚索 = max=BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头), min=BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)}= max=BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), min=BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)}
易大师 = max=BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), min=BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)}

案例代码三十三Collectors.maxBy

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作collect(Collector.Mapping())
 */
public class Demo33StreamCollectorsMaxBy {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Optional<AuthorReq> maxAge1 = authors.stream().max(Comparator.comparingInt(AuthorReq::getAge));//方式一
        Optional<AuthorReq> maxAge2 = authors.stream().collect(Collectors.maxBy(Comparator.comparingInt(AuthorReq::getAge)));//方式二
        System.out.println(maxAge2.get());
    }
}

[13 max&min 最值]

id方法名方法作用请求参数返回值类型操作类型
13max和min最大(小)值
Comparator<T> :比较器,指定比较大小的方式
Optional<T> :返回泛型T,包装一层Optional判空,这样就可以允许返回空值
Comparator<T>Optional<T>终止

案例13.1 最小年龄

需求: 找出年龄最小的作者的信息

执行结果:

AuthorReq(id=3, name=, engName=, age=0, intro=, books=[BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)])

案例代码三十四min  年龄最小

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作max&min
 */
public class Demo34StreamMaxAndMin {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Optional<AuthorReq> minAge1 = authors.stream().min(Comparator.comparingInt(AuthorReq::getAge));//方式一
        Optional<AuthorReq> minAge2 = authors.stream().collect(Collectors.minBy(Comparator.comparingInt(AuthorReq::getAge)));//方式二
        System.out.println(minAge1.get());
    }
}

案例13.2 最高(低)分作品

需求: 找出每个作者(按姓名分组)的最高分作品最低分作品

执行结果:

蒙多 = max=BookReq(id=2, name=一个人不能死在同一把刀下, category=个人成长,爱情, score=99, intro=讲述如何从失败中明悟真理), min=BookReq(id=1, name=刀的两侧是光明与黑暗, category=哲学,爱情, score=88, intro=用一把刀划分了爱恨)}
亚索 = max=BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头), min=BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)}= max=BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), min=BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)}
易大师 = max=BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), min=BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容)}

案例代码三十五max&min  最高(低)分作品

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.common.dto.BookReq;
import com.groupies.jdk8.day02.service.StreamService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作max&min
 */
public class Demo35StreamMaxAndMin {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        //按作者姓名聚合
        Map<String, List<AuthorReq>> collect = authors.stream().collect(Collectors.groupingBy(AuthorReq::getName));
        //取每个姓名的最高分作品和最低分作品信息
        Map<String, BookMap> map = collect.keySet().stream().collect(Collectors.toMap(key -> key, key -> new BookMap
               (collect.get(key).stream().map(AuthorReq::getBooks).flatMap(List::stream).max((b1, b2) -> b1.getScore().compareTo(b2.getScore())).get(),
                collect.get(key).stream().map(AuthorReq::getBooks).flatMap(List::stream).min((b1, b2) -> b1.getScore().compareTo(b2.getScore())).get())));
        map.keySet().forEach(key -> System.out.println(key + " = " + map.get(key)));
    }

    /**
     * 存放最高分作品和最低分作品
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class BookMap {
        public BookReq max;
        public BookReq min;

        @Override
        public String toString() {
            return "max=" + max + ", min=" + min +'}';
        }
    }
}

[14 find 查找]


id方法名方法作用请求参数返回值类型操作类型
14find查找
1. findFirst:如果一个集合数据是有序的,而且你要查找符合条件的第一条数据。这时用findFirst是比较合适的。
2. findAny:返回任意一条数据,不关心顺序。该方法不能保证获取的一定是流中的第一条数据。且在并行流中,findAny限制更少。
-Optional<T>终止

案例14.1 findFirst 查找第一个

需求: 获取一个年龄最小的作家,并输出他的姓名。

执行结果:

AuthorReq(id=3, name=, engName=, age=0, intro=, books=[BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)])
...
AuthorReq(id=3, name=, engName=, age=0, intro=, books=[BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)])

案例代码三十六findFirst  查找第一个

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作Find
 */
public class Demo36StreamFindFirst {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        System.out.println(authors.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).findFirst().get());//方式一
        System.out.println("...");
        authors.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).limit(1).forEach(System.out::println);//方式二
    }
}


案例14.2 findAny 查找任一个

需求: 获取任意一个年龄大于1的作家,如果存在就输出他的名字。

注意: 这里需要使用并行流,才能看出效果,每次执行结果可能不同。

执行结果:

//第一次
Optional[AuthorReq(id=3, name=易大师, engName=MasterYi, age=14, intro=剑圣, books=[BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)])]
//第二次
Optional[AuthorReq(id=3, name=易大师, engName=MasterYi, age=14, intro=剑圣, books=[BookReq(id=5, name=你的剑就是我的剑, category=爱情, score=56, intro=无法想象一个武者能对他的伴侣这么的宽容), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?), BookReq(id=6, name=风与剑, category=个人传记, score=100, intro=两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?)])]
//第三次
Optional[AuthorReq(id=2, name=亚索, engName=Yasuo, age=15, intro=托儿所, books=[BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头), BookReq(id=3, name=那风吹不到的地方, category=哲学, score=85, intro=带你用思维去领略世界的尽头), BookReq(id=4, name=吹或不吹, category=爱情,个人传记, score=56, intro=一个哲学家的恋爱观注定很难把他所在的时代理解)])]
//...

案例代码三十七findAny  查找任一个

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Optional;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作Find
 */
public class Demo37StreamFindAny {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Optional<AuthorReq> any = authors.parallelStream()
                .filter(authorReq -> authorReq.getAge() > 1)
                .findAny();

        System.out.println(any);
    }
}

[15 match 匹配]

id方法名方法作用请求参数返回值类型操作类型
15match匹配
1. allMatch 全部匹配,则返回true
2. anymatch 任一一条匹配,则返回true
3. nonematch 全部不匹配,则返回true
Predicate<T>boolean终止

案例15.1 allMatch 全部匹配

需求: 判断是否有年龄在29以上的作家。

执行结果:

true

案例代码三十八allMatch  全部匹配

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作AllMatch
 */
public class Demo38StreamAllMatch {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        boolean flag = authors.stream()
                .anyMatch(author -> author.getAge() > 29);
        System.out.println(flag);
    }
}

案例15.2 anyMatch 任一匹配

需求: 判断是否所有的作家都是成年人。

执行结果:

false

案例代码三十九anyMatch  任一匹配

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作AnyMatch
 */
public class Demo39StreamAnyMatch {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        boolean flag = authors.stream()
                .allMatch(author -> author.getAge() >= 18);
        System.out.println(flag);
    }
}

案例15.3 noneMatch 全不匹配

需求: 判断作家是否都没有超过100岁的。

执行结果:

true

案例代码四十noneMatch  全不匹配

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2022/01/04
 * @introduction 终结操作NoneMatch
 */
public class Demo40StreamNoneMatch {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        boolean flag = authors.stream()
                .noneMatch(author -> author.getAge() > 100);
        System.out.println(flag);
    }
}

[16 reduce 聚合]


案例16.1 reduce(accumulator)

id方法名方法作用请求参数返回值类型操作类型
16reduce(accumulator)聚合
聚合的含义就是将多个值经过特定计算之后得到单个值
参数1. accumulator 计算方式
BinaryOperator<T>accumulatorOptional<T>终止

需求: 使用reduce(accumulator)求所有作者年龄的和。如果不为null则打印。

执行结果:

91

案例代码四十一reduce  年龄求和

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Optional;

/**
 * @author GroupiesM
 * @date 2022/01/05
 * @introduction 终结操作reduce(accumulator)
 */
public class Demo41StreamReduce {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Optional<Integer> sumOptional = authors.stream()
                .map(AuthorReq::getAge)
                .reduce(Integer::sum);
        //如果不为null->打印,null->不处理
        sumOptional.ifPresent(System.out::println);
    }
}

案例16.2 reduce(accumulator,combiner)

id方法名方法作用请求参数返回值类型操作类型
reduce(accumulator,
combiner)
accumulator 计算方式
combiner 初始值
T identity,
BinaryOperator<T>accumulator
T终止

需求: 使用reduce(accumulator,combiner)求所有作者中年龄的最大值。如果接过为空,则打印-1

执行结果:

0

案例代码四十二reduce  年龄最大值

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Optional;

/**
 * @author GroupiesM
 * @date 2022/01/05
 * @introduction 终结操作reduce(accumulator,combiner)
 */
public class Demo42StreamReduce {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) {
        List<AuthorReq> authors = streamService.getAuthors();
        Optional<Integer> minOptional = authors.stream()
                .map(AuthorReq::getAge)
                .reduce((result, element) -> result > element ? element : result);
        //如果接过为空,则打印-1
        System.out.println(minOptional.orElse(-1));
    }
}

案例16.3 reduce(identity,accumulator,combiner)

id方法名方法作用请求参数返回值类型操作类型
reduce(identity,
accumulator,combiner)
详情查看=>博客
identity 初始值
accumulator 计算方式
combiner 并行计算下 合并各个线程的计算结果
U identity,
BiFunction<U,T,U>accumulator
BinaryOperator<U>combiner
Optional<U>终止

需求: 使用reduce(identity,accumulator,combiner)求所有作者中年龄的最大值。

执行结果:

true

案例代码四十三reduce  年龄求和

package com.groupies.jdk8.day02.practice;

import com.groupies.jdk8.day02.common.dto.AuthorReq;
import com.groupies.jdk8.day02.service.StreamService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * @author GroupiesM
 * @date 2022/01/05
 * @introduction 终结操作reduce(identity,accumulator,combiner)
 */
public class Demo43StreamReduce {
    @Autowired
    private static StreamService streamService;

    public static void main(String[] args) throws Throwable {
        List<AuthorReq> authors = streamService.getAuthors();
        Integer max = authors.stream()
                .map(AuthorReq::getAge)
                .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
        System.out.println(max);
    }
}

5. 高级用法


5.1 基本数据类型优化

  我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。
  即使我们操作的是整数小数,但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候,你就不能无视这个时间损耗了。
  所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。

  例如: mapToInt, mapToLong, mapToDouble, flatMapToInt, flatMapToDouble 等。

    @Autowired
    private static StreamService streamService;
    
    public static void main(String[] args) {
        List<Author> authors = streamService.getAuthors();
        authors.stream()
                .map(author -> author.getAge())
                .map(age -> age + 10)//Integer -> 自动拆箱 -> int+10 -> int -> 自动装箱 ->Integer
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);

        authors.stream()
                .mapToInt(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);
    }

5.2 并行流

  当流中有大量元素时,我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完全。如果我们自己去用代码实现的话其实会非常的复杂,并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话,我们只需要修改一个方法的调用就可以使用并行流来帮我们实现,从而提高效率。
  parallel方法可以把串行流转换成并行流。

    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = stream.parallel()
                .peek(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer num) {
                        System.out.println(num+Thread.currentThread().getName());
                    }
                })
                .filter(num -> num > 5)
                .reduce((result, ele) -> result + ele)
                .get();
        System.out.println(sum);
    }

​ 也可以通过parallelStream直接获取并行流对象。

        List<Author> authors = streamService.getAuthors();
        authors.parallelStream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);

6. idea调试Stream流


在stream流处打断点,然后进入debug调试,可以更形象的理解 “流式处理”
在这里插入图片描述

点击按钮,查看数据链路详情
在这里插入图片描述

distinct过程详情在这里插入图片描述

filter过程详情在这里插入图片描述


22/01/05

M

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值