前言
在Java 8之前,处理集合数据通常需要写大量的样板代码。比如过滤、映射、聚合等操作,都需要手动编写循环和条件判断。这种方式不仅冗长,而且容易出错。Stream API的引入,就是为了简化这些操作,提供一种声明式的、函数式编程风格的数据处理方式。
什么是Stream?
Stream不是数据结构,它是一个数据处理管道 。你可以把它想象成一个工厂的流水线,原材料(数据源)进入流水线,经过各种工序(中间操作),最终产出成品(终端操作结果)。关键点是:Stream本身不存储数据,它只是对数据源进行操作。
我们先来看一个对比:
java
// Java 8 之前的写法
List<String> names = new ArrayList<>();
for (Person person : persons) {
if (person.getAge() > 18) {
names.add(person.getName().toUpperCase());
}
}
// Stream API 写法
List<String> names = persons.stream()
.filter(person -> person.getAge() > 18)
.map(Person::getName)
.map(String::toUpperCase)
.collect(Collectors.toList());
可以看到,Stream API的代码更加简洁、易读,而且更接近自然语言的表达方式。
Stream的三个核心概念
- 数据源:可以是集合、数组等''
- 中间操作:返回一个新的Stream,可以链式调用
- 终端操作:产生结果或副作用,结束Stream的处理
graph LR
A[数据源: List/Array] --> B[中间操作: filter/map/sorted]
B --> C[中间操作: map/flatMap/distinct]
C --> D[终端操作: collect/forEach/count]
D --> E[结果: List/void/long]
一、核心接口详解
1.1 Stream接口继承关系
classDiagram
BaseStream <|-- Stream
BaseStream <|-- IntStream
BaseStream <|-- LongStream
BaseStream <|-- DoubleStream
Stream <|-- IntStream
Stream <|-- LongStream
Stream <|-- DoubleStream
java
// BaseStream接口定义了基本的流操作
public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable {
// 关闭流(用于并行流)
void close();
boolean isParallel();
S sequential();
S parallel();
S unordered();
S onClose(Runnable closeHandler);
}
// Stream接口继承BaseStream,添加了具体的流操作
public interface Stream<T> extends BaseStream<T, Stream<T>> {
// 中间操作
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Stream<T> distinct();
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> peek(Consumer<? super T> action);
Stream<T> limit(long maxSize);
Stream<T> skip(long n);
// 终端操作
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
Optional<T> findFirst();
Optional<T> findAny();
}
二、中间操作详解
中间操作是Stream API的核心,它们都是惰性求值的,只有在终端操作执行时才会真正执行。
2.1 filter - 过滤
java
// 理论:根据Predicate条件过滤元素
// 源码:Stream<T> filter(Predicate<? super T> predicate);
@Test
public void testFilter() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 筛选偶数
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("偶数: " + evenNumbers); // [2, 4, 6, 8, 10]
// 案例:过滤有效用户
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 17, false),
new User("Charlie", 30, true)
);
List<User> activeAdultUsers = users.stream()
.filter(user -> user.isActive() && user.getAge() >= 18)
.collect(Collectors.toList());
System.out.println("活跃成年用户数: " + activeAdultUsers.size());
}
static class User {
private String name;
private int age;
private boolean active;
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// getters...
public String getName() { return name; }
public int getAge() { return age; }
public boolean isActive() { return active; }
}
2.2 map - 映射
java
// 理论:将每个元素转换为另一个元素
// 源码:<R> Stream<R> map(Function<? super T, ? extends R> mapper);
@Test
public void testMap() {
List<String> names = Arrays.asList("alice", "bob", "charlie");
// 转换为大写
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println("大写名字: " + upperNames); // [ALICE, BOB, CHARLIE]
// 案例:提取用户ID
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 17, false),
new User("Charlie", 30, true)
);
List<String> userIds = users.stream()
.map(user -> "user_" + user.getName().toLowerCase())
.collect(Collectors.toList());
System.out.println("用户ID: " + userIds);
// 复杂映射:计算年龄平方
List<Integer> ageSquares = users.stream()
.map(user -> user.getAge() * user.getAge())
.collect(Collectors.toList());
System.out.println("年龄平方: " + ageSquares);
}
2.3 flatMap - 扁平化映射
java
// 理论:将多个Stream合并为一个Stream
// 源码:<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
@Test
public void testFlatMap() {
// 场景:处理嵌套集合
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d", "e"),
Arrays.asList("f")
);
// 扁平化处理
List<String> flatList = nestedList.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
System.out.println("扁平化结果: " + flatList); // [a, b, c, d, e, f]
// 案例:提取订单中的所有商品
List<Order> orders = Arrays.asList(
new Order(Arrays.asList("商品1", "商品2")),
new Order(Arrays.asList("商品3")),
new Order(Arrays.asList("商品4", "商品5", "商品6"))
);
List<String> allProducts = orders.stream()
.flatMap(order -> order.getProducts().stream())
.collect(Collectors.toList());
System.out.println("所有商品: " + allProducts);
}
static class Order {
private List<String> products;
public Order(List<String> products) {
this.products = products;
}
public List<String> getProducts() {
return products;
}
}
2.4 distinct - 去重
java
// 理论:根据equals和hashCode去重
// 源码:Stream<T> distinct();
@Test
public void testDistinct() {
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 5, 5);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("去重后: " + distinctNumbers); // [1, 2, 3, 4, 5]
// 案例:获取唯一的用户城市
List<User> users = Arrays.asList(
new User("Alice", 25, true, "北京"),
new User("Bob", 17, false, "上海"),
new User("Charlie", 30, true, "北京"),
new User("David", 22, true, "广州")
);
List<String> uniqueCities = users.stream()
.map(User::getCity)
.distinct()
.collect(Collectors.toList());
System.out.println("唯一城市: " + uniqueCities); // [北京, 上海, 广州]
}
static class User {
private String name;
private int age;
private boolean active;
private String city;
public User(String name, int age, boolean active, String city) {
this.name = name;
this.age = age;
this.active = active;
this.city = city;
}
// getters...
public String getName() { return name; }
public int getAge() { return age; }
public boolean isActive() { return active; }
public String getCity() { return city; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(name, user.name) && Objects.equals(city, user.city);
}
@Override
public int hashCode() {
return Objects.hash(name, city);
}
}
2.5 sorted - 排序
java
// 理论:对流元素进行排序
// 源码:
// Stream<T> sorted();
// Stream<T> sorted(Comparator<? super T> comparator);
@Test
public void testSorted() {
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3);
// 自然排序
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println("自然排序: " + sortedNumbers); // [1, 2, 3, 5, 8, 9]
// 自定义排序
List<Integer> reverseSorted = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println("逆序排序: " + reverseSorted);
// 案例:按用户年龄排序
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 17, false),
new User("Charlie", 30, true)
);
List<User> sortedUsers = users.stream()
.sorted(Comparator.comparing(User::getAge))
.collect(Collectors.toList());
System.out.println("按年龄排序: " + sortedUsers.stream()
.map(user -> user.getName() + "(" + user.getAge() + ")")
.collect(Collectors.toList()));
// 多字段排序
List<User> multiSortedUsers = users.stream()
.sorted(Comparator.comparing(User::isActive, Comparator.reverseOrder())
.thenComparing(User::getAge))
.collect(Collectors.toList());
System.out.println("按活跃状态和年龄排序: " + multiSortedUsers);
}
static class User {
private String name;
private int age;
private boolean active;
public User(String name, int age, boolean active) {
this.name = name;
this.age = age;
this.active = active;
}
// getters...
public String getName() { return name; }
public int getAge() { return age; }
public boolean isActive() { return active; }
}
2.6 peek - 调试操作
java
// 理论:对每个元素执行操作,主要用于调试
// 源码:Stream<T> peek(Consumer<? super T> action);
@Test
public void testPeek() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = numbers.stream()
.peek(n -> System.out.println("处理前: " + n))
.filter(n -> n % 2 == 0)
.peek(n -> System.out.println("过滤后: " + n))
.map(n -> n * n)
.peek(n -> System.out.println("映射后: " + n))
.collect(Collectors.toList());
System.out.println("最终结果: " + result);
// 案例:日志记录
List<User> users = Arrays.asList(
new User("Alice", 25, true),
new User("Bob", 17, false),
new User("Charlie", 30, true)
);
List<User> processedUsers = users.stream()
.peek(user -> log.info("开始处理用户: " + user.getName()))
.filter(user -> user.getAge() >= 18)
.peek(user -> log.info("用户符合年龄要求: " + user.getName()))
.map(user -> {
user.setActive(true);
return user;
})
.peek(user -> log.info("用户已激活: " + user.getName()))
.collect(Collectors.toList());
}
2.7 limit & skip - 限制和跳过
java
// 理论:limit限制元素数量,skip跳过元素数量
// 源码:
// Stream<T> limit(long maxSize);
// Stream<T> skip(long n);
@Test
public void testLimitSkip() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 限制前5个元素
List<Integer> firstFive = numbers.stream()
.limit(5)
.collect(Collectors.toList());
System.out.println("前5个: " + firstFive); // [1, 2, 3, 4, 5]
// 跳过前3个元素
List<Integer> skipThree = numbers.stream()
.skip(3)
.collect(Collectors.toList());
System.out.println("跳过前3个: " + skipThree); // [4, 5, 6, 7, 8, 9, 10]
// 分页处理
int pageSize = 3;
int pageNum = 2;
List<Integer> pageData = numbers.stream()
.skip((long) (pageNum - 1) * pageSize)
.limit(pageSize)
.collect(Collectors.toList());
System.out.println("第2页数据: " + pageData); // [4, 5, 6]
// 案例:获取热门商品前10名
List<Product> products = generateProducts(100); // 假设生成100个商品
List<Product> top10Products = products.stream()
.sorted(Comparator.comparing(Product::getSales).reversed())
.limit(10)
.collect(Collectors.toList());
System.out.println("热门商品前10名数量: " + top10Products.size());
}
static class Product {
private String name;
private int sales;
public Product(String name, int sales) {
this.name = name;
this.sales = sales;
}
// getters...
public String getName() { return name; }
public int getSales() { return sales; }
}
private List<Product> generateProducts(int count) {
List<Product> products = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < count; i++) {
products.add(new Product("商品" + i, random.nextInt(1000)));
}
return products;
}
三、终端操作详解
终端操作是Stream的终点,它们会触发整个流水线的执行。
3.1 forEach - 遍历
java
// 理论:对每个元素执行操作
// 源码:void forEach(Consumer<? super T> action);
@Test
public void testForEach() {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 串行处理
names.stream()
.forEach(name -> System.out.println("Hello, " + name));
// 并行处理(注意顺序不保证)
names.parallelStream()
.forEach(name -> System.out.println("Parallel: " + name));
// 保证顺序的并行处理
names.parallelStream()
.forEachOrdered(name -> System.out.println("Ordered: " + name));
// 案例:批量发送邮件
List<User> users = Arrays.asList(
new User("Alice", "alice@example.com"),
new User("Bob", "bob@example.com"),
new User("Charlie", "charlie@example.com")
);
users.stream()
.filter(User::isActive)
.forEach(user -> {
// 模拟发送邮件
System.out.println("发送邮件给: " + user.getEmail());
// emailService.send(user.getEmail(), "欢迎使用我们的服务");
});
}
static class User {
private String name;
private String email;
private boolean active = true;
public User(String name, String email) {
this.name = name;
this.email = email;
}
// getters...
public String getName() { return name; }
public String getEmail() { return email; }
public boolean isActive() { return active; }
public void setActive(boolean active) { this.active = active; }
}
3.2 collect - 收集
java
// 理论:将流元素收集到集合中
// 源码:<R, A> R collect(Collector<? super T, A, R> collector);
@Test
public void testCollect() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 收集到List
List<Integer> evenList = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 收集到Set
Set<Integer> evenSet = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toSet());
// 收集到Map
Map<String, Integer> numberMap = numbers.stream()
.collect(Collectors.toMap(
n -> "number_" + n, // key mapper
n -> n // value mapper
));
System.out.println("Map: " + numberMap);
// 分组收集
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("偶数分组: " + partitioned.get(true));
System.out.println("奇数分组: " + partitioned.get(false));
// 案例:按部门分组用户
List<User> users = Arrays.asList(
new User("Alice", "IT"),
new User("Bob", "HR"),
new User("Charlie", "IT"),
new User("David", "Finance")
);
Map<String, List<User>> usersByDepartment = users.stream()
.collect(Collectors.groupingBy(User::getDepartment));
System.out.println("按部门分组:");
usersByDepartment.forEach((dept, userList) -> {
System.out.println(dept + ": " + userList.stream()
.map(User::getName)
.collect(Collectors.toList()));
});
// 统计收集
IntSummaryStatistics stats = numbers.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
System.out.println("统计信息: " + stats);
}
static class User {
private String name;
private String department;
public User(String name, String department) {
this.name = name;
this.department = department;
}
// getters...
public String getName() { return name; }
public String getDepartment() { return department; }
}
3.3 reduce - 归约
java
// 理论:将流元素归约为单个值
// 源码:
// T reduce(T identity, BinaryOperator<T> accumulator);
// Optional<T> reduce(BinaryOperator<T> accumulator);
@Test
public void testReduce() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 求和
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("求和: " + sum); // 15
// 求积
int product = numbers.stream()
.reduce(1, (a, b) -> a * b);
System.out.println("求积: " + product); // 120
// 找最大值
Optional<Integer> max = numbers.stream()
.reduce(Integer::max);
System.out.println("最大值: " + max.orElse(0)); // 5
// 字符串连接
List<String> words = Arrays.asList("Hello", "World", "Java");
String sentence = words.stream()
.reduce("", (a, b) -> a + " " + b)
.trim();
System.out.println("句子: " + sentence); // Hello World Java
// 案例:计算订单总金额
List<Order> orders = Arrays.asList(
new Order(100.0),
new Order(200.5),
new Order(50.25)
);
double totalAmount = orders.stream()
.map(Order::getAmount)
.reduce(0.0, Double::sum);
System.out.println("订单总金额: " + totalAmount);
// 案例:合并用户权限
List<User> users = Arrays.asList(
new User("Alice", Arrays.asList("read", "write")),
new User("Bob", Arrays.asList("read", "delete")),
new User("Charlie", Arrays.asList("write", "execute"))
);
Set<String> allPermissions = users.stream()
.map(User::getPermissions)
.flatMap(List::stream)
.collect(Collectors.toSet());
System.out.println("所有权限: " + allPermissions);
}
static class Order {
private double amount;
public Order(double amount) {
this.amount = amount;
}
public double getAmount() { return amount; }
}
static class User {
private String name;
private List<String> permissions;
public User(String name, List<String> permissions) {
this.name = name;
this.permissions = permissions;
}
public List<String> getPermissions() { return permissions; }
}
3.4 match操作 - 匹配检查
java
// 理论:检查元素是否满足条件
// 源码:
// boolean anyMatch(Predicate<? super T> predicate);
// boolean allMatch(Predicate<? super T> predicate);
// boolean noneMatch(Predicate<? super T> predicate);
@Test
public void testMatch() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 是否存在偶数
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
System.out.println("存在偶数: " + hasEven); // true
// 是否全是偶数
boolean allEven = numbers.stream()
.allMatch(n -> n % 2 == 0);
System.out.println("全是偶数: " + allEven); // false
// 是否没有负数
boolean noNegative = numbers.stream()
.noneMatch(n -> n < 0);
System.out.println("没有负数: " + noNegative); // true
// 案例:验证用户权限
List<User> users = Arrays.asList(
new User("Alice", true, true),
new User("Bob", true, false),
new User("Charlie", false, true)
);
// 检查是否有活跃用户
boolean hasActiveUser = users.stream()
.anyMatch(User::isActive);
System.out.println("有活跃用户: " + hasActiveUser);
// 检查是否所有用户都已验证
boolean allVerified = users.stream()
.allMatch(User::isVerified);
System.out.println("所有用户已验证: " + allVerified);
// 检查是否没有被封禁用户
boolean noBannedUser = users.stream()
.noneMatch(User::isBanned);
System.out.println("没有被封禁用户: " + noBannedUser);
}
static class User {
private String name;
private boolean active;
private boolean verified;
private boolean banned = false;
public User(String name, boolean active, boolean verified) {
this.name = name;
this.active = active;
this.verified = verified;
}
public User(String name, boolean active, boolean verified, boolean banned) {
this.name = name;
this.active = active;
this.verified = verified;
this.banned = banned;
}
// getters...
public boolean isActive() { return active; }
public boolean isVerified() { return verified; }
public boolean isBanned() { return banned; }
}
3.5 find操作 - 查找
java
// 理论:查找元素
// 源码:
// Optional<T> findFirst();
// Optional<T> findAny();
@Test
public void testFind() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 查找第一个元素
Optional<Integer> first = numbers.stream()
.findFirst();
System.out.println("第一个元素: " + first.orElse(0)); // 1
// 查找任意偶数
Optional<Integer> anyEven = numbers.stream()
.filter(n -> n % 2 == 0)
.findAny();
System.out.println("任意偶数: " + anyEven.orElse(0));
// 案例:查找特定用户
List<User> users = Arrays.asList(
new User("Alice", 25),
new User("Bob", 17),
new User("Charlie", 30)
);
// 查找第一个成年用户
Optional<User> firstAdult = users.stream()
.filter(user -> user.getAge() >= 18)
.findFirst();
if (firstAdult.isPresent()) {
System.out.println("第一个成年用户: " + firstAdult.get().getName());
}
// 在并行流中查找
Optional<User> anyAdult = users.parallelStream()
.filter(user -> user.getAge() >= 18)
.findAny();
System.out.println("任意成年用户: " + anyAdult.map(User::getName).orElse("无"));
}
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
// getters...
public String getName() { return name; }
public int getAge() { return age; }
}
四、高级用法
4.1 并行流
并行流是Stream API的一个重要特性,它可以在多核CPU上并行处理数据,提高性能。
java
@Test
public void testParallelStream() {
List<Integer> numbers = IntStream.range(1, 1000000)
.boxed()
.collect(Collectors.toList());
// 串行处理
long startTime = System.currentTimeMillis();
long sum1 = numbers.stream()
.mapToLong(n -> n * n)
.sum();
long serialTime = System.currentTimeMillis() - startTime;
// 并行处理
startTime = System.currentTimeMillis();
long sum2 = numbers.parallelStream()
.mapToLong(n -> n * n)
.sum();
long parallelTime = System.currentTimeMillis() - startTime;
System.out.println("串行结果: " + sum1 + ", 耗时: " + serialTime + "ms");
System.out.println("并行结果: " + sum2 + ", 耗时: " + parallelTime + "ms");
System.out.println("加速比: " + (double) serialTime / parallelTime);
// 案例:大数据处理
List<DataRecord> records = generateLargeDataset(1000000);
// 并行处理数据转换
List<ProcessedRecord> processedRecords = records.parallelStream()
.map(this::processRecord)
.filter(Objects::nonNull)
.collect(Collectors.toList());
System.out.println("处理记录数: " + processedRecords.size());
}
private DataRecord processRecord(DataRecord record) {
// 模拟耗时操作
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
return new ProcessedRecord(record.getId(), record.getValue() * 2);
}
private List<DataRecord> generateLargeDataset(int size) {
return IntStream.range(0, size)
.mapToObj(i -> new DataRecord(i, Math.random()))
.collect(Collectors.toList());
}
static class DataRecord {
private int id;
private double value;
public DataRecord(int id, double value) {
this.id = id;
this.value = value;
}
// getters...
public int getId() { return id; }
public double getValue() { return value; }
}
static class ProcessedRecord {
private int id;
private double processedValue;
public ProcessedRecord(int id, double processedValue) {
this.id = id;
this.processedValue = processedValue;
}
// getters...
public int getId() { return id; }
public double getProcessedValue() { return processedValue; }
}
4.2 自定义Collector
在复杂的业务场景中,我们经常需要自定义Collector来满足特定的收集需求。
java
@Test
public void testCustomCollector() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 自定义Collector:收集到不可变List
List<Integer> immutableList = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collector.of(
ArrayList::new, // supplier
ArrayList::add, // accumulator
(list1, list2) -> { // combiner
list1.addAll(list2);
return list1;
},
Collections::unmodifiableList // finisher
));
System.out.println("不可变列表: " + immutableList);
// 自定义Collector:分组统计
Map<Boolean, Long> countByEven = numbers.stream()
.collect(Collector.of(
() -> new HashMap<Boolean, Long>() {{
put(true, 0L);
put(false, 0L);
}},
(map, item) -> map.merge(item % 2 == 0, 1L, Long::sum),
(map1, map2) -> {
map2.forEach((key, value) -> map1.merge(key, value, Long::sum));
return map1;
}
));
System.out.println("分组统计: " + countByEven);
// 案例:自定义Collector用于业务统计
List<Order> orders = Arrays.asList(
new Order("Alice", 100.0, "Electronics"),
new Order("Bob", 50.0, "Books"),
new Order("Charlie", 200.0, "Electronics"),
new Order("David", 30.0, "Books")
);
// 按类别统计订单
Map<String, OrderStatistics> statsByCategory = orders.stream()
.collect(Collectors.groupingBy(
Order::getCategory,
Collector.of(
OrderStatistics::new,
OrderStatistics::accept,
OrderStatistics::combine,
Collector.Characteristics.IDENTITY_FINISH
)
));
System.out.println("按类别统计:");
statsByCategory.forEach((category, stats) -> {
System.out.println(category + " - 数量: " + stats.getCount() +
", 总额: " + stats.getTotalAmount() +
", 平均值: " + stats.getAverageAmount());
});
}
static class Order {
private String customer;
private double amount;
private String category;
public Order(String customer, double amount, String category) {
this.customer = customer;
this.amount = amount;
this.category = category;
}
// getters...
public String getCustomer() { return customer; }
public double getAmount() { return amount; }
public String getCategory() { return category; }
}
static class OrderStatistics {
private long count = 0;
private double totalAmount = 0.0;
public void accept(Order order) {
count++;
totalAmount += order.getAmount();
}
public OrderStatistics combine(OrderStatistics other) {
this.count += other.count;
this.totalAmount += other.totalAmount;
return this;
}
public double getAverageAmount() {
return count == 0 ? 0 : totalAmount / count;
}
// getters...
public long getCount() { return count; }
public double getTotalAmount() { return totalAmount; }
}