本文目录导读:

Java 中 Collectors.partitioningBy 案例详解
partitioningBy 是 Java Stream API 中的一个收集器,用于根据 Predicate 条件将元素分为两组(true 和 false),它返回一个 Map<Boolean, List<T>>。
基础用法
案例1:按数字奇偶性分区
import java.util.*;
import java.util.stream.Collectors;
public class BasicPartitioning {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 按奇偶数分区
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));
// 输出:
// 偶数: [2, 4, 6, 8, 10]
// 奇数: [1, 3, 5, 7, 9]
}
}
案例2:按字符串长度分区
public class StringPartitioning {
public static void main(String[] args) {
List<String> words = Arrays.asList("Java", "Python", "C", "JS", "Kotlin", "Rust");
// 按长度是否大于3分区
Map<Boolean, List<String>> partitioned = words.stream()
.collect(Collectors.partitioningBy(word -> word.length() > 3));
System.out.println("长度>3的单词: " + partitioned.get(true));
System.out.println("长度<=3的单词: " + partitioned.get(false));
// 输出:
// 长度>3的单词: [Java, Python, Kotlin, Rust]
// 长度<=3的单词: [C, JS]
}
}
使用下游收集器
案例3:分区后进行二次处理
import java.util.*;
import java.util.stream.Collectors;
public class DownstreamCollector {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 分区后统计每组的个数
Map<Boolean, Long> countByParity = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0,
Collectors.counting()
));
System.out.println("偶数个数: " + countByParity.get(true));
System.out.println("奇数个数: " + countByParity.get(false));
// 分区后求和
Map<Boolean, Integer> sumByParity = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0,
Collectors.summingInt(Integer::intValue)
));
System.out.println("偶数和: " + sumByParity.get(true));
System.out.println("奇数和: " + sumByParity.get(false));
}
}
案例4:复杂对象的分区处理
import java.util.*;
import java.util.stream.Collectors;
class Person {
private String name;
private int age;
private double salary;
public Person(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() { return name; }
public int getAge() { return age; }
public double getSalary() { return salary; }
@Override
public String toString() {
return name + "(" + age + "岁, " + salary + "元)";
}
}
public class ObjectPartitioning {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("张三", 25, 8000),
new Person("李四", 35, 15000),
new Person("王五", 30, 12000),
new Person("赵六", 28, 6000),
new Person("孙七", 45, 20000)
);
// 按是否超过30岁分区
Map<Boolean, List<Person>> byAge = people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() > 30));
System.out.println("30岁以上: " + byAge.get(true));
System.out.println("30岁及以下: " + byAge.get(false));
// 分区并按工资排序
Map<Boolean, List<Person>> bySalarySorted = people.stream()
.collect(Collectors.partitioningBy(
p -> p.getSalary() > 10000,
Collectors.collectingAndThen(
Collectors.toList(),
list -> list.stream()
.sorted(Comparator.comparingDouble(Person::getSalary))
.collect(Collectors.toList())
)
));
System.out.println("\n高薪员工(按工资排序): " + bySalarySorted.get(true));
System.out.println("低薪员工(按工资排序): " + bySalarySorted.get(false));
}
}
组合使用多个条件
案例5:多个条件组合分区
public class ComplexCondition {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
// 同时满足:能被2整除且大于5
Map<Boolean, List<Integer>> complex = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0 && n > 5
));
System.out.println("满足条件(偶数且>5): " + complex.get(true));
System.out.println("不满足条件: " + complex.get(false));
}
}
与其他收集器组合
案例6:分区后使用 joining
public class JoiningPartitioning {
public static void main(String[] args) {
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
// 分区后连接成字符串
Map<Boolean, String> joined = words.stream()
.collect(Collectors.partitioningBy(
s -> s.length() > 5,
Collectors.joining(", ", "[", "]")
));
System.out.println("长单词: " + joined.get(true));
System.out.println("短单词: " + joined.get(false));
// 分区后转成 Set
Map<Boolean, Set<String>> sets = words.stream()
.collect(Collectors.partitioningBy(
s -> s.contains("a"),
Collectors.toSet()
));
System.out.println("含'a'的单词: " + sets.get(true));
System.out.println("不含'a'的单词: " + sets.get(false));
}
}
实际应用场景
案例7:数据分析
public class DataAnalysis {
public static void main(String[] args) {
List<Double> scores = Arrays.asList(85.5, 92.0, 78.5, 95.5, 60.0, 45.5, 88.0, 70.5, 55.0, 90.5);
// 及格/不及格分组
Map<Boolean, List<Double>> byPass = scores.stream()
.collect(Collectors.partitioningBy(score -> score >= 60));
// 统计各组成绩的平均值
Map<Boolean, Double> averages = scores.stream()
.collect(Collectors.partitioningBy(
score -> score >= 60,
Collectors.averagingDouble(Double::doubleValue)
));
System.out.println("及格人数: " + byPass.get(true).size());
System.out.println("不及格人数: " + byPass.get(false).size());
System.out.println("及格平均分: " + averages.get(true));
System.out.println("不及格平均分: " + averages.get(false));
}
}
性能优化与实际注意事项
案例8:处理大量数据
public class PerformanceExample {
public static void main(String[] args) {
// 生成100万个随机数
Random random = new Random();
List<Integer> data = random.ints(1_000_000, 1, 1000000)
.boxed()
.collect(Collectors.toList());
// 方式1:partitioningBy(推荐)
long start1 = System.currentTimeMillis();
Map<Boolean, Long> count1 = data.stream()
.collect(Collectors.partitioningBy(
n -> n > 500000,
Collectors.counting()
));
long end1 = System.currentTimeMillis();
System.out.println("partitioningBy耗时: " + (end1 - start1) + "ms");
// 方式2:groupingBy(等价的替代方案)
long start2 = System.currentTimeMillis();
Map<String, Long> count2 = data.stream()
.collect(Collectors.groupingBy(
n -> n > 500000 ? "big" : "small",
Collectors.counting()
));
long end2 = System.currentTimeMillis();
System.out.println("groupingBy耗时: " + (end2 - start2) + "ms");
}
}
嵌套分区
案例9:多级分区
public class NestedPartitioning {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
// 先按奇偶分,再按是否大于6分
Map<Boolean, Map<Boolean, List<Integer>>> nested = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0, // 外层:奇偶
Collectors.partitioningBy(n -> n > 6) // 内层:大小
));
System.out.println("偶数且>6: " + nested.get(true).get(true));
System.out.println("偶数且<=6: " + nested.get(true).get(false));
System.out.println("奇数且>6: " + nested.get(false).get(true));
System.out.println("奇数且<=6: " + nested.get(false).get(false));
}
}
partitioningBy 的优势:
- 语义明确:专门用于二元分组
- 性能优化:比
groupingBy更高效,因为只有两个键 - 结果可预测:返回的 Map 一定包含 true/false 两个键
- 支持下游收集器:可以进行复杂的二次处理
适用场景:
- 布尔条件的二元分类(是否、存在、满足等)
- 数据统计分析(分组统计、分组求值)
- 复杂业务规则的条件划分
注意事项:
- 返回的 Map 类型是
HashMap,不保证顺序 - 如果不需要下游收集器,直接使用即可
- 适合处理二元逻辑,多分类请使用
groupingBy