1.概述
Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于简化操作集合或者数组的
Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大方式操作集合或者数组中的数据,代码更简洁,可读性更好

2.Stream流对象的获取
2.1.集合转Stream
单列集合底层接口Collection提供stream()方法可以将集合转成Stream default Stream<E> stream() 。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* Stream流对象的获取方式
* 1. 单列集合 : Collection 接口中定义的 stream 方法即可
* 2. 数组 :
* - Arrays.stream(数组)
* - Stream.of(数组)
*/
public class Demo1 {
public static void main(String[] args) {
//1. 单列集合的Stream流对象获取
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add("孙七");
list.add("周八");
list.add("吴九");
list.add("郑十");
// 获取流对象
Stream<String> stream1 = list.stream();
System.out.println(stream1);
}
}2.2.数组转Stream
数组的工具类Arrays提供stream(数组)方法可以将数组转成Stream static <T> Stream<T> stream(T[] array)
Stream里面的静态方法of也可以完成, 但是只限于引用类型的数组, (这个底层采用的是工具类Arrays提供stream(数组)方法)
static Stream<E> of(T ... element);
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* Stream流对象的获取方式
* 1. 单列集合 : Collection 接口中定义的 stream 方法即可
* 2. 数组 :
* - Arrays.stream(数组)
* - Stream.of(数组)
*/
public class Demo1 {
public static void main(String[] args) {
//1. 单列集合的Stream流对象获取
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.add("孙七");
list.add("周八");
list.add("吴九");
list.add("郑十");
// 获取流对象
Stream<String> stream1 = list.stream();
System.out.println(stream1);
// 2. 数组的Stream流对象获取
String[] arr = {"张三", "李四", "王五", "赵六", "孙七", "周八", "吴九", "郑十"};
//2.1 Arrays.stream(数组)
Stream<String> stream2 = Arrays.stream(arr);
System.out.println(stream2);
//2.2 Stream.of(数组)
Stream<String> stream3 = Stream.of(arr);
System.out.println(stream3);
}
}3.Stream流的功能
3.1.中间方法
中间方法返回的依旧是流。
| 返回值 | 方法名 参数 | 作用 |
|---|---|---|
| Stream<R> | filter(Predicate<? super T> predicate) | 根据条件过滤元素 |
| Stream | limit(long maxSize) | 限制取前几个元素 |
| Stream | skip(long n) | 跳过前几个元素 |
| Stream | distinct() | 去重,采用的Hash去重 |
| Stream | sorted() | 自然排序 |
| Stream | sorted(比较器) | 比较器排序 |
| Stream<T> | static concat(Stream<? extends T> a, Stream<? extends T> b) | 将两个流合并成一个流 |
| Stream<R> | map(Function<? super T,? extends R> mapper) | 将流的元素修改或者修改成另外一种类型 |
| Stream<R> | flatMap(Function<? super T,? extends Stream<? extends R>> mapper) | 处理嵌套集合或者数组,将所有元素处理成为一条流 |
请注意
每一次创建的流只能被使用一次,再次使用会报错。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Demo4 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
Stream<Integer> stream1 = list.stream();
stream1.filter(i -> i > 2);
// 下面的这行代码在运行的时候会报错
stream1.filter(i -> i > 3);
}
}3.1.1.filter过滤
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> res = list.stream().filter(num -> num > 3).collect(Collectors.toList());
System.out.println(res); // [4,5,6]
}
}3.1.2.map一对一映射转换
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<String> list = Arrays.asList("java", "stream");
List<Integer> res = list.stream().map(s -> s.length()).collect(Collectors.toList());
System.out.println(res); // [4,6]
}
}3.1.3.flatMap-扁平化(高维转低维)
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<String> list = Arrays.asList("a,b,c", "1,2");
List<String> res = list.stream()
.flatMap(s -> Arrays.stream(s.split(","))) // 切割并合并
.collect(Collectors.toList());
System.out.println(res); // [a, b, c, 1, 2]
}
}3.1.4.sorted () 自然排序
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(3, 1, 4, 2);
List<Integer> res = list.stream()
.sorted().collect(Collectors.toList());
System.out.println(res); // [1,2,3,4]
}
}3.1.5. sorted (Comparator) 自定义排序
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(3, 1, 4, 2);
List<Integer> res = list.stream()
.sorted((o1, o2) -> Integer.compare(o2, o1)) // 降序
.collect(Collectors.toList());
System.out.println(res); // [4,3,2,1]
}
}3.1.6. distinct () 去重
对象或者复杂数据类型的去重依赖 equals & hashCode。
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Demo1 {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
userList.add(new User("K001", "张三"));
userList.add(new User("K001", "张三"));
userList.add(new User("K001", "张三"));
userList.add(new User("K002", "王五"));
List<User> list = userList.stream().distinct().toList();
System.out.println(list); // [User{idCard='K001', name='张三'}, User{idCard='K002', name='王五'}]
}
}
class User {
private String idCard;
private String name;
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String idCard, String name) {
this.idCard = idCard;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(idCard, user.idCard) && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(idCard, name);
}
@Override
public String toString() {
return "User{" +
"idCard='" + idCard + '\'' +
", name='" + name + '\'' +
'}';
}
}3.1.7.limit (long n) 截取前 n 个
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> res = list.stream()
.limit(3).collect(Collectors.toList());
System.out.println(res); // [1,2,3]
}
}3.1.8.skip (long n) 跳过前 n 个
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> res = list.stream()
.skip(2).collect(Collectors.toList());
System.out.println(res); // [3,4,5]
}
}3.1.9.peek (Consumer) 调试查看,不改变流
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Demo1 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
List<Integer> res = list.stream()
.peek(num -> System.out.println("原始值:" + num))
.map(n -> n * 2)
.collect(Collectors.toList());
System.out.println(res); // [2,4,6]
}
}3.2.终端操作-数据采集方法
特点: 调用结束后, 不再返回Stream对象, 不能继续使用Stream功能。
| 返回值 | 方法名参数 | 作用 |
|---|---|---|
| void | forEach(Consumer<? super T> action) | 遍历Stream中的元素 |
| long | count() | 获取流中元素的个数 |
| List<T> | toList() | 将流中的数据采集为List并返回 |
| Optional<T> | findAny() | 获取流中任意元素并返回 |
| Optional<T> | findFirst() | 获取流中第一个元素并返回 |
Optional是一个存储单个数据的数据容器, 方便对有可能为空的数据进行处理, 其中可能有元素也可能没有 , 其中有一些对空值判断和获取的常用方法
isPresent(): 元素是否存在isEmpty(): 元素是否不存在get(): 获取其中的元素数据orElse(默认值): 获取元素数据, 如果不存在返回默认值
3.2.1.collect (Collector) 收集(最常用)
常用的功能参考如下:
List<String> list = Arrays.asList("A","B","C");
List<User> userList = Arrays.asList(new User(1,"张三"), new User(2,"李四"));
// toList / toSet
List<String> listRes = list.stream().collect(Collectors.toList());
Set<String> setRes = list.stream().collect(Collectors.toSet());
// toMap(key重复抛异常)
Map<Integer,String> mapRes = userList.stream().collect(Collectors.toMap(u -> u.getId(), u -> u.getName()));
// toMap 处理key重复(第三参合并规则)
Map<Integer,String> mapRes2 = userList.stream().collect(Collectors.toMap(u -> u.getId(), u -> u.getName(), (oldVal,newVal) -> newVal));
// groupingBy 分组
Map<Integer, List<User>> groupMap = userList.stream().collect(Collectors.groupingBy(u -> u.getId()));
// partitioningBy 分区(boolean,两组)
// partitioningBy和groupingBy的区别(重点!)
// partitioningBy:分组依据是布尔表达式 Predicate,永远只有 2 组,`Map<Boolean, ...>`
// groupingBy:分组依据是任意值Function,可以分成多组,`Map<K, List<T>>`
// 如果只是二选一划分,优先用 `partitioningBy`,语义更清晰。
Map<Boolean, List<User>> partitionMap = userList.stream().collect(Collectors.partitioningBy(u -> u.getId() > 1));
// summarizingInt 统计(总数/平均/最大最小)
IntSummaryStatistics stats = userList.stream().collect(Collectors.summarizingInt(u -> u.getId()));
System.out.println("总数:"+stats.getCount()+",最大值:"+stats.getMax());聚合统计功能
- counting():计数,返回 Long
- summingInt / summingLong / summingDouble:求和
- averagingInt / averagingLong / averagingDouble:求平均值
- maxBy (比较器):取最大值(返回 Optional)
- minBy (比较器):取最小值(返回 Optional)
- summarizingInt:一次性拿到 总数、总和、最大、最小、平均值(
IntSummaryStatistics)
聚合统计的案例:
import java.util.*;
import java.util.stream.Collectors;
public class StreamCollectStatsDemo {
// 实体:学生
static class Student {
private String name;
private int age;
private int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() { return name; }
public int getAge() { return age; }
public int getScore() { return score; }
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";
}
}
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
new Student("张三", 18, 85),
new Student("李四", 20, 92),
new Student("王五", 19, 76),
new Student("赵六", 18, 88),
new Student("孙七", 20, 95)
);
// 1. counting():统计元素总数,返回Long
Long count = studentList.stream()
.collect(Collectors.counting());
System.out.println("总人数 counting:" + count);
// 2. summingInt:求和(分数总和)
Integer sumScore = studentList.stream()
.collect(Collectors.summingInt(Student::getScore));
System.out.println("总分 summingInt:" + sumScore);
// summingLong / summingDouble 用法一样,只是返回类型不同
// summingLong 适用于long属性,summingDouble适用于double
// 3. averagingInt:求平均分,返回Double
Double avgScore = studentList.stream()
.collect(Collectors.averagingInt(Student::getScore));
System.out.println("平均分 averagingInt:" + avgScore);
// 4. maxBy(比较器):最高分学生,返回Optional,防止空集合
Optional<Student> maxScoreStu = studentList.stream()
.collect(Collectors.maxBy(Comparator.comparingInt(Student::getScore)));
maxScoreStu.ifPresent(stu -> System.out.println("最高分学生 maxBy:" + stu));
// 5. minBy(比较器):最低分学生
Optional<Student> minScoreStu = studentList.stream()
.collect(Collectors.minBy(Comparator.comparingInt(Student::getScore)));
minScoreStu.ifPresent(stu -> System.out.println("最低分学生 minBy:" + stu));
// 6. summarizingInt:一次性获取【总数、总和、平均值、最大值、最小值】
IntSummaryStatistics scoreStats = studentList.stream()
.collect(Collectors.summarizingInt(Student::getScore));
System.out.println("\n==== summarizingInt 一次性统计 ====");
System.out.println("数量:" + scoreStats.getCount());
System.out.println("总和:" + scoreStats.getSum());
System.out.println("平均值:" + scoreStats.getAverage());
System.out.println("最大值:" + scoreStats.getMax());
System.out.println("最小值:" + scoreStats.getMin());
}
}字符串拼接
把字符串流拼接成一个大字符串,只适用于元素是 String。
List<String> names = Arrays.asList("张三","李四","王五");
String res = names.stream().collect(Collectors.joining());
// 分隔符
String res2 = names.stream().collect(Collectors.joining(","));
// 分隔符 + 前缀 + 后缀
String res3 = names.stream().collect(Collectors.joining(",", "[", "]"));
// [张三,李四,王五]3.2.2.forEach (Consumer) 遍历
List<String> list = Arrays.asList("A","B","C");
list.stream().forEach(s -> System.out.println(s));3.2.3.count () 元素数量 long
long count = Arrays.asList(1,2,3,4).stream().count();
System.out.println(count); // 43.2.4.anyMatch /allMatch/noneMatch 匹配判断
List<Integer> list = Arrays.asList(1,2,3,4);
boolean anyMatch = list.stream().anyMatch(n -> n > 3); // true 任一满足
boolean allMatch = list.stream().allMatch(n -> n > 0); // true 全部满足
boolean noneMatch = list.stream().noneMatch(n -> n > 10); // true 全都不满足3.2.5.findFirst () /findAny () 查找 Optional
List<Integer> list = Arrays.asList(5,1,2);
Optional<Integer> first = list.stream().findFirst(); // 第一个
Optional<Integer> any = list.stream().findAny(); // 任意一个(并行更快)
first.ifPresent(num -> System.out.println(num));
// 得到的是一个Optional的数据,为了数据的安全使用可以参考下面这些方法
// isPresent() : 元素是否存在
// isEmpty(): 元素是否不存在
// get(): 获取其中的元素数据
// orElse(默认值): 获取元素数据, 如果不存在返回默认值3.2.6.max(Comparator) / min(Comparator)
List<Integer> list = Arrays.asList(3,1,9,5);
Optional<Integer> max = list.stream().max((o1,o2) -> Integer.compare(o1,o2));
Optional<Integer> min = list.stream().min((o1,o2) -> Integer.compare(o1,o2));
max.ifPresent(num -> System.out.println(num)); // 9
min.ifPresent(num -> System.out.println(num)); // 13.2.7.reduce () 归约聚合
List<Integer> list = Arrays.asList(1,2,3,4);
// 带初始值
Integer sum = list.stream().reduce(0, (a,b) -> a + b);
System.out.println(sum); // 10
// 无初始值
Optional<Integer> sumOpt = list.stream().reduce((a,b) -> a + b);
sumOpt.ifPresent(num -> System.out.println(num));
// 三参重载:identity + mapper + combiner(并行流用)
List<User> userList = Arrays.asList(new User(1,"张三"), new User(2,"李四"));
Integer totalId = userList.stream().reduce(0, (acc, user) -> acc + user.getId(), (acc1, acc2) -> acc1 + acc2);
System.out.println(totalId); // 3注意
- Stream流元素的操作"不会影响"原集合或者数组,
注明: "不会影响"指的是不会影响集合元素个数, 如果操作元素地址内部的数据肯定会受影响
- Stream流对象只能使用一次, 但凡被操作了, 就不能再使用了, Stream主打的就是链式编程解决所有的问题, 所以禁止我们对流进行二次操作
3.3.案例练习参考
需求参考
使用Stream流集合提供的商品列表数据完成下列需求:
使用Stream流集合提供的商品列表数据完成下列需求:
- 打印所有商品详情
- 获取评分最高的前3个商品并打印
- 获取销量最高的前3个商品
- 获取价格大于5000且评分高于4.8分的商品信息
- 获取手机数码分类下评分高于4.8分的商品信息
- 获取销量大于100且价格高于5000且评分低于4.8分的商品名称
- 获取所有的分类名称(去重)
- 获取有商品销量大于100的分类名称以及分类下商品的总销量
- 计算所有商品的总销量
- 计算每个分类下的商品总销量
- 获取库存低于100,且销量高于100的商品信息
- 计算所有商品的平均价格
- 计算每个分类下的商品平均价格
- 计算每个分类下商品的平均评分
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
public class Demo0 {
public static void main(String[] args) {
// 构造真实商品数据列表(10个商品)
List<Product1> products = List.of(
new Product1("iPhone 15 Pro", 8999.00, 120, 356, "手机数码", 4.9),
new Product1("华为 Mate 60", 6499.00, 200, 480, "手机数码", 4.8),
new Product1("小米14 Ultra", 5999.00, 150, 290, "手机数码", 4.7),
new Product1("联想小新Pro16", 5299.00, 80, 145, "电脑办公", 4.6),
new Product1("MacBook Air M3", 8499.00, 60, 98, "电脑办公", 4.9),
new Product1("戴尔XPS 13", 7899.00, 45, 67, "电脑办公", 4.5),
new Product1("索尼WH-1000XM5", 2299.00, 300, 520, "影音娱乐", 4.8),
new Product1("AirPods Pro 2", 1899.00, 400, 680, "影音娱乐", 4.7),
new Product1("大疆Osmo Pocket 3", 3499.00, 90, 130, "摄影摄像", 4.6),
new Product1("佳能EOS R6 Mark II", 15999.00, 25, 18, "摄影摄像", 4.9)
);
// 1. 打印所有商品详情
test1(products);
// 2. 获取评分最高的前3个商品并打印
test2(products);
// 3. 获取销量最高的前3个商品
test3(products);
// 4. 获取价格大于5000且评分高于4.8分的商品信息
test4(products);
// 5. 获取手机数码分类下评分高于4.8分的商品信息
test5(products);
// 6. 获取销量大于100且价格高于5000且评分低于4.8分的商品名称
test6(products);
// 7. 获取所有的分类名称(去重)
test7(products);
// 8. 获取有商品销量大于100的分类名称以及分类下商品的总销量
test8(products);
// 9. 计算所有商品的总销量
test9(products);
// 10. 计算每个分类下的商品总销量
test10(products);
// 11. 获取库存低于100,且销量高于100的商品信息
test11(products);
// 12. 计算所有商品的平均价格
test12(products);
// 13. 计算每个分类下的商品平均价格
test13(products);
// 14. 计算每个分类下商品的平均评分
test14(products);
}
public static void test1(List<Product1> product1s) {
// 1. 打印所有商品详情
}
public static void test2(List<Product1> product1s) {
// 2. 获取评分最高的前3个商品并打印
}
public static void test3(List<Product1> product1s) {
// 3. 获取销量最高的前3个商品
}
public static void test4(List<Product1> product1s) {
// 4. 获取价格大于5000且评分高于4.8分的商品信息
}
public static void test5(List<Product1> product1s) {
// 5. 获取手机数码分类下评分高于4.8分的商品信息
}
public static void test6(List<Product1> product1s) {
// 6. 获取销量大于100且价格高于5000且评分低于4.8分的商品名称
}
public static void test7(List<Product1> product1s) {
// 7. 获取所有的分类名称(去重)
}
public static void test8(List<Product1> product1s) {
// 8. 获取有商品销量大于100的分类名称以及分类下商品的总销量
}
public static void test9(List<Product1> product1s) {
// 9. 计算所有商品的总销量
}
public static void test10(List<Product1> product1s) {
// 10. 计算每个分类下的商品总销量
}
public static void test11(List<Product1> product1s) {
// 11. 获取库存低于100,且销量高于100的商品信息
}
public static void test12(List<Product1> product1s) {
// 12. 计算所有商品的平均价格
}
public static void test13(List<Product1> product1s) {
// 13. 计算每个分类下的商品平均价格
}
public static void test14(List<Product1> product1s) {
// 14. 计算每个分类下商品的平均评分
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class Product1 {
private String name; // 商品名称
private double price; // 商品价格
private Integer stock; // 商品库存
private Integer sales; // 商品销量
private String category; // 商品分类
private double rating; // 商品评分
}4.原始类型流
原始类型流 IntStream / LongStream / DoubleStream。主要是为了避免自动拆箱装箱,提升性能。
import java.util.OptionalInt;
import java.util.stream.IntStream;
public class PrimitiveStreamDemo {
public static void main(String[] args) {
List<User> userList = Arrays.asList(new User(1,"张三"), new User(2,"李四"));
// mapToInt / mapToLong / mapToDouble 转原始流
userList.stream().mapToInt(u -> u.getId()).forEach(num -> System.out.println(num));
userList.stream().mapToLong(u -> u.getId()).forEach(val -> System.out.println(val));
userList.stream().mapToDouble(u -> u.getId() * 1.0).forEach(val -> System.out.println(val));
// mapToObj 原始流转对象流
IntStream.range(1,5).mapToObj(i -> new User(i,"name"+i)).forEach(u -> System.out.println(u));
// range 左闭右开 [1,10);rangeClosed 闭区间 [1,10]
IntStream.range(1,10).forEach(num -> System.out.print(num));
IntStream.rangeClosed(1,10).forEach(num -> System.out.print(num));
// 原始流特有操作
int sum = IntStream.rangeClosed(1,5).sum();
OptionalInt max = IntStream.rangeClosed(1,5).max();
OptionalInt min = IntStream.rangeClosed(1,5).min();
IntStream.rangeClosed(1,5).average().ifPresent(val -> System.out.println(val));
// boxed() 原始流 → 包装类型流 Stream<Integer>
IntStream.range(1,5).boxed().collect(Collectors.toList()).forEach(num -> System.out.println(num));
}
}