1.简介
(1).概念
Stream是数据渠道,用于操作数据源所生成的元素序列,它可以实现对集合的复杂操作,例如过滤、排序和映射等。
集合是一种静态的数据结构,存储在内存中,而Stream是通过CPU来实现计算的(并不会存储数据)。Stream不会改变源对象,而是返回一个新的持有结果的Stream(不可变)。

(2).为什么要使用Stream
在实际开发中,项目中的很多数据都来源于关系型数据库(例如MySQL、Oracle等),使用SQL的条件语句就可以实现对数据的筛选、过滤等操作。但也有很多数据来源于非关系型数据库(例如Redis、MongoDB等),想要处理这些数据,往往需要在Java层面去操作。
而Stream API就提供了对集合中数据的操作,类似于SQL执行的数据库查询。简单来说,Stream API提供了一种高效且易于使用的数据处理方式。

(3).Stream操作的执行流程

  1. 创建Stream对象:通过一个数据源(例如集合、数组)创建一个流。
  2. 中间操作:一个中间的链式操作,对数据源的数据进行处理(例如过滤、排序等),直到执行终止操作才执行(Stream操作是延迟执行的)。
  3. 终止操作:一旦执行终止操作,就执行中间的链式操作,并产生结果。

(4).涉及到的对象定义

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Book {
    public String title;
    public String author;
    public Integer pages;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return Objects.equals(title, book.title) &&
                Objects.equals(author, book.author)
                && Objects.equals(pages,book.pages);
    }

    @Override
    public int hashCode() {
        return Objects.hash(title, author, pages);
    }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Author {
    public String name;

    public String title;
}

2.创建Stream
(1).返回一个串行流

public class CreateStreamByCollection {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("hello");
        stringList.add("world");
        
        //通过集合获取串行Stream
        Stream<String> stream = stringList.stream();
        //打印元素
        stream.forEach(str -> System.out.println(str));
    }
}

(2).返回一个并行流

public class CreateParallelStreamByCollection {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("hello");
        stringList.add("world");
        
        //通过集合获取并行Stream
        Stream<String> parallelStream = stringList.parallelStream();
        //打印元素
        parallelStream.forEach(str -> System.out.println(str));
    }
}

串行流和并行流的区别:串行流从集合中取数据是按照集合的顺序取的,而并行流是并行操作的,获取到的数据是无序的。

3.中间操作
(1).简介
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。在终止操作时会一次性全部处理这些中间操作,称为“惰性求值”。

(2).filter

public class StreamFilter {
    public static void main(String[] args) {
    	//从流中清除某些元素
        //初始化一个包含3个Book对象的数组,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223)
        );
        
        bookList.stream()
                .filter(book -> book.getPages() >= 500)
                .forEach(System.out::println); //触发终止操作才能执行中间操作,遍历列表中元素并打印
    }
}

(3).distinct

public class StreamFilter {
    public static void main(String[] args) {
    	//通过流生成元素的hashCode和equals()方法去除重复元素
        //初始化一个包含5个Book对象的数组,其中有2个重复的元素,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223),
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365)
        );
        //创建流
        System.out.println("去重前,集合中的元素为:");
        bookList.stream()
                .forEach(System.out::println);

        System.out.println("去重后,集合中的元素为:");
        //创建一个新流,然后调用distinct()方法去除重复元素,并调用终止操作打印集合中元素
        bookList.stream()
                .distinct()
                .forEach(System.out::println);
    }
}

(4).limit

public class StreamLimit {
    public static void main(String[] args) {
    	//截取流的前几个元素
        //初始化一个包含3个Book对象的数组,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223)
        );
        //创建一个流,通过limit()方法截取前2个元素,最后遍历列表中元素并打印
        bookList.stream()
                .limit(2)
                .forEach(System.out::println);
    }
}

(5).skip

public class StreamSkip {
    public static void main(String[] args) {
    	//返回一个扔掉了前n个元素的流,与limit互补。若流中元素不足n个,则返回一个空流
        //初始化一个包含3个Book对象的数组,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223)
        );
        //创建一个流,通过skip()方法扔掉前2个元素,最后遍历列表中元素并打印
        bookList.stream()
                .skip(2)
                .forEach(System.out::println);
    }
}

(6).map

public class StreamMapping {
    public static void main(String[] args) {
    	//接收一个方法作为参数,该方法会被应用到每个元素上,并将其映射成一个新的元素
        //初始化一个包含3个Book对象的数组,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223)
        );

        //将bookList中name为张三的元素,转化为Author对象,并最终转化为authorList
        List<Author> authorList = bookList.stream()
                .map(book -> {
                    Author author = new Author();
                    if ("张三".equals(book.getAuthor())) {
                        author.setName(book.getAuthor());
                        author.setTitle(book.getTitle());
                    }
                    return author;
                }).collect(Collectors.toList());
        //过滤掉name为null的元素并打印name为张三的元素
        authorList.stream()
                .filter(author -> Objects.nonNull(author.getName()))
                .forEach(System.out::println);
}
public class StreamMapping {
    public static void main(String[] args) {
        //初始化一个包含3个Book对象的数组,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223)
        );
        //将book元素的author字段收集为一个字符串数组
        List<String> authorStrings = bookList.stream()
                .map(b -> b.getAuthor())
                .collect(Collectors.toList());
        //打印元素
        System.out.println(authorStrings);
    }
}
public class StreamMapping {
    public static void main(String[] args) {
        //创建一个包含小写字母元素的字符串列表
        List<String> stringList = Arrays.asList("php", "js", "python", "java");
        /*
         * 调用map()方法,将String的toUpperCase()方法作为参数
         * 这个方法会被应用到每个元素上,映射成一个新元素,最后打印映射后的元素
         */
        stringList.stream()
                .map(String::toUpperCase)
                .forEach(System.out::println);
    }
}

(7).sorted

public class SortedByComparator {
    public static void main(String[] args) {
    	//按照从大到小顺序排序
        //初始化一个包含3个Book对象的数组,并转化为List
        List<Book> bookList= Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223)
        );
        //定制排序,并打印每个元素
        bookList.stream()
                .sorted((b1, b2) -> -Integer.compare(b1.getPages(), b2.getPages()))
                .forEach(System.out::println);
    }
}
public class SortedByComparator {
    public static void main(String[] args) {
        //初始化一个包含6个乱序值的Integer型数组,并转化为List
        List<Integer> integers = Arrays.asList(10, 12, 9, 8, 20, 1);
        //定制排序,并打印每个元素
        integers.stream()
        .sorted((i1, i2) -> -Integer.compare(i1, i2))
        .forEach(System.out::println);
    }
}
public class Sorted {
    public static void main(String[] args) {
    	//按照自然顺序排序
        //初始化一个包含6个乱序值的Integer型数组,并转化为List
        List<Integer> integers = Arrays.asList(10, 12, 9, 8, 20, 1);
        //调用sorted()方法自然排序,并打印每个元素
        integers.stream()
                .sorted()
                .forEach(System.out::println);
    }
}

4.终止操作
(1).简介
执行终止操作会从流的流水线上生成结果,其结果可以是任何不是流的值,例如List、String或者void等。

(2).匹配

public class StreamMatch {
    public static void main(String[] args) {
        //初始化一个包含5个Book对象的数组,其中有2个重复的元素,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223),
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365)
        );
        //使用allMatch()检查是否所有元素匹配已知条件,如果匹配,则返回true,否则返回false
        boolean b1 = bookList.stream().allMatch(book -> book.getPages() > 300);
        if (b1) {//预计输出
            System.out.println("列表中所有元素的页码数都大于300");
        } else {
            System.out.println("列表中有元素的页码数小于等于300");
        }

        //使用anyMatch()检查是否至少有一个元素匹配已知条件,如果匹配,则返回true,否则返回false
        boolean b2 = bookList.stream().anyMatch(book -> book.getPages() > 1200);
        if (b2) {//预计输出
            System.out.println("列表中至少存在一个的元素的页码数大于1200");
        } else {
            System.out.println("列表中不存在元素页码数大于1200");
        }

        //使用noneMath()检查是否没元素匹配已知条件的,如果匹配,则返回true,否则返回false
        boolean b3 = bookList.stream().noneMatch(book -> book.getPages() > 1200);
        if (b3) {
            System.out.println("列表中不存在元素页码数大于1200");
        } else {//预计输出
            System.out.println("列表中至少存在一个的元素的页码数大于1200");
        }
    }
}

(3).查找

public class StreamFind {
    public static void main(String[] args) {
        //初始化一个包含5个Book对象的数组,其中有2个重复的元素,并转化为List
        List<Book> bookList = Arrays.asList(
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365),
                new Book("Java编程", "李四", 223),
                new Book("C++编程", "张三", 1216),
                new Book("C#编程", "张三", 365)
        );
        //使用findFirst()获取当前流中的第一个元素
        Optional<Book> first = bookList.stream().findFirst();
        System.out.println("列表中第一个元素为:" + first);

        //使用findAny()获取当前流中的任意一个元素
        Optional<Book> any = bookList.stream().findAny();
        System.out.println("列表中任意一个元素为:" + any);

        //使用count()获取当前流中的元素总数
        long count = bookList.stream().count();
        System.out.println("列表中元素总数为" + count);

        //使用max(Comparator c)获取流中最大值
        Optional<Book> max = bookList.stream().max(Comparator.comparingInt(Book::getPages));
        System.out.println("列表中元素页码最大值为" + max.get().getPages());

        //使用min(Comparator c)获取流中最小值
        Optional<Book> min = bookList.stream().min(Comparator.comparingInt(Book::getPages));
        System.out.println("列表中元素页码最小值为" + min.get().getPages());
    }
}

(4).收集

@Slf4j
public class StreamCollect {
    public static void main(String[] args) {
        //创建一个包含7个Integer型的数据,并转为List
        List<Integer> integerList = Arrays.asList(10, 12, 9, 8, 20, 1, 10);
        //将List流转换为其他Set集合
        Set<Integer> collect = integerList.stream().collect(Collectors.toSet());
        System.out.println(collect);
    }
}