1.简介
(1).概念
Stream是数据渠道,用于操作数据源所生成的元素序列,它可以实现对集合的复杂操作,例如过滤、排序和映射等。
集合是一种静态的数据结构,存储在内存中,而Stream是通过CPU来实现计算的(并不会存储数据)。Stream不会改变源对象,而是返回一个新的持有结果的Stream(不可变)。
(2).为什么要使用Stream
在实际开发中,项目中的很多数据都来源于关系型数据库(例如MySQL、Oracle等),使用SQL的条件语句就可以实现对数据的筛选、过滤等操作。但也有很多数据来源于非关系型数据库(例如Redis、MongoDB等),想要处理这些数据,往往需要在Java层面去操作。
而Stream API就提供了对集合中数据的操作,类似于SQL执行的数据库查询。简单来说,Stream API提供了一种高效且易于使用的数据处理方式。
(3).Stream操作的执行流程
- 创建Stream对象:通过一个数据源(例如集合、数组)创建一个流。
- 中间操作:一个中间的链式操作,对数据源的数据进行处理(例如过滤、排序等),直到执行终止操作才执行(Stream操作是延迟执行的)。
- 终止操作:一旦执行终止操作,就执行中间的链式操作,并产生结果。
(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);
}
}