单一key-value对象工具-org.apache.commons.lang3.tuple.Pair
org.apache.commons.lang3.tuple.Pair是 Apache Commons Lang 库中一个非常实用的类,它提供了一种简洁、类型安全的方式来表示和操作由两个相关对象组成的“对”或“二元组”。它解决了 Java 标准库中没有内置通用二元组类型的问题。
核心概念与特点:
-
表示一对值: 它封装了两个对象,通常称为“左”(
left)和“右”(right)。这两个对象可以是相同类型,也可以是不同类型。 -
泛型支持:
Pair<L, R>是泛型类,其中:-
L表示左侧元素的类型。 -
R表示右侧元素的类型。 -
这提供了编译时的类型安全,避免了使用
Object类型带来的强制类型转换和潜在错误。
-
-
不可变性:
Pair本身是一个抽象类。你通常使用它的具体实现ImmutablePair<L, R>或MutablePair<L, R>:-
ImmutablePair: 一旦创建,其left和right值就不能再更改。线程安全,推荐在大多数情况下使用。 -
MutablePair: 创建后,可以通过setLeft和setRight方法修改其值。适用于需要改变内部状态的场景。
-
-
实用工厂方法:
Pair类提供了静态工厂方法Pair.of(L left, R right)来方便地创建实例。这个方法通常会返回一个ImmutablePair(具体实现可能因版本而异,但语义是不可变的)。 -
访问方法:
-
L getLeft(): 获取左侧元素。 -
R getRight(): 获取右侧元素。 -
L getKey(): 与getLeft()相同(为了兼容类似 Map.Entry 的接口)。 -
R getValue(): 与getRight()相同(为了兼容类似 Map.Entry 的接口)。 -
(仅
MutablePair)void setLeft(L left): 设置左侧元素。 -
(仅
MutablePair)void setRight(R right): 设置右侧元素。 -
(仅
MutablePair)R setValue(R value): 设置右侧元素(为了兼容类似 Map.Entry 的接口)。
-
-
比较与相等性:
Pair实现了Comparable<Pair>和覆盖了equals()和hashCode()方法。比较和相等性判断基于left和right的值(按顺序比较left然后right)。 -
toString(): 提供了格式良好的字符串表示,通常是(left, right)。
为什么使用 Pair?
-
返回多个值: 当一个方法需要返回两个相关的结果时,使用
Pair比创建一个专门的 DTO 类更简洁,也比使用数组或Object[]更类型安全。 -
临时聚合数据: 在算法或流处理中,临时将两个值组合在一起进行处理或传递非常方便。
-
替代简单的 Map.Entry: 当你需要一个键值对但不想创建完整的
Map时,Pair是一个轻量级的选择。Pair实现了Map.Entry接口。 -
提高代码可读性: 使用
getLeft()和getRight()(或命名更贴切的变量)比使用get(0)和get(1)(如数组或列表)更能清晰地表达元素的含义。
使用示例:
-
基本创建与访问 (使用
ImmutablePair):import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair;public class PairExample {public static void main(String[] args) {// 创建包含姓名和年龄的 Pair (推荐方式:使用工厂方法)Pair<String, Integer> person = ImmutablePair.of("Alice", 30);// 或者直接构造 ImmutablePair (工厂方法通常更简洁)// Pair<String, Integer> person = new ImmutablePair<>("Alice", 30);// 访问元素String name = person.getLeft(); // 或者 person.getKey()Integer age = person.getRight(); // 或者 person.getValue()System.out.println("Name: " + name + ", Age: " + age); // 输出: Name: Alice, Age: 30System.out.println(person); // 输出: (Alice,30)} } -
使用
MutablePair修改值:import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair;public class MutablePairExample {public static void main(String[] args) {// 创建一个可变的 Pair 表示坐标点MutablePair<Double, Double> point = MutablePair.of(10.5, 20.0);System.out.println("Initial Point: " + point); // 输出: (10.5,20.0)// 修改坐标值point.setLeft(15.75); // 移动 X 坐标point.setRight(25.5); // 移动 Y 坐标// 或者 point.setValue(25.5); 注意:setValue 修改的是 right/valueSystem.out.println("Updated Point: " + point); // 输出: (15.75,25.5)} } -
作为方法返回值:
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair;public class MathUtils {/*** 计算一个数的平方和立方。* @param number 输入的数字* @return 一个 Pair,left 是平方,right 是立方。*/public static Pair<Double, Double> calculateSquareAndCube(double number) {double square = number * number;double cube = number * number * number;return ImmutablePair.of(square, cube);}public static void main(String[] args) {Pair<Double, Double> result = calculateSquareAndCube(3.0);System.out.println("Square: " + result.getLeft()); // 输出: Square: 9.0System.out.println("Cube: " + result.getRight()); // 输出: Cube: 27.0} } -
在集合中使用 (例如,模拟简单 Map 或存储关联对):
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.List;public class PairCollectionExample {public static void main(String[] args) {// 创建一个存储 (城市, 人口) 对的列表List<Pair<String, Long>> cityPopulation = new ArrayList<>();cityPopulation.add(ImmutablePair.of("New York", 8336817L));cityPopulation.add(ImmutablePair.of("London", 8982000L));cityPopulation.add(ImmutablePair.of("Tokyo", 13929286L));// 遍历并打印for (Pair<String, Long> city : cityPopulation) {System.out.println(city.getLeft() + " has population: " + city.getRight());}// 输出:// New York has population: 8336817// London has population: 8982000// Tokyo has population: 13929286} } -
与 Java Stream API 结合使用:
import org.apache.commons.lang3.tuple.Pair; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors;public class PairStreamExample {public static void main(String[] args) {List<String> names = Arrays.asList("Alice", "Bob", "Charlie");List<Integer> ages = Arrays.asList(30, 25, 35);// 将两个列表按索引组合成 Pair 列表 (假设两个列表长度相同)List<Pair<String, Integer>> people = names.stream().map(name -> Pair.of(name, ages.get(names.indexOf(name)))).collect(Collectors.toList());// 或者更高效的方式 (如果索引顺序一致)List<Pair<String, Integer>> peopleAlt = names.stream().map(name -> {int index = names.indexOf(name);return Pair.of(name, ages.get(index));}).collect(Collectors.toList());// 处理 Pair 流people.forEach(pair ->System.out.println(pair.getLeft() + " is " + pair.getRight() + " years old"));} }
重要注意事项:
-
选择不可变还是可变: 优先使用
ImmutablePair。除非你明确需要在创建后修改left或right的值,否则不可变性有助于避免意外修改、提高线程安全性和代码可预测性。使用Pair.of()通常返回不可变对。 -
命名: 虽然元素名为
left和right,但它们可以表示任何逻辑上成对的概念(键/值、输入/输出、坐标 X/Y、姓名/年龄等)。在代码中,将Pair赋值给一个描述性变量名(如nameAgePair,coordinate)可以大大提高可读性。 -
替代方案:
-
自定义类: 如果这个“对”在你的领域模型中是一个重要的、有明确业务含义的概念(例如
Point,Person),或者需要包含更多字段或方法,那么定义一个专门的类通常是更好的选择,因为它能提供更强的语义和封装。 -
Java 16+ Record: Java 16 引入的
record是定义不可变数据载体的简洁方式,非常适合替代简单的Pair或 DTO。例如record NameAge(String name, int age) {}。 -
数组或 List: 类型不安全,可读性差(依赖索引)。
-
AbstractMap.SimpleEntry/AbstractMap.SimpleImmutableEntry: Java 标准库中的类似概念,但 API 不如 Commons Lang 的Pair一致和常用,且名称侧重于 Map 的键值对。
-
-
依赖: 使用
Pair需要将 Apache Commons Lang3 库添加到你的项目依赖中(例如 Maven 的commons-lang3)。
总结:
org.apache.commons.lang3.tuple.Pair(及其实现 ImmutablePair/MutablePair)是一个轻量级、类型安全的工具类,用于表示和操作两个相关联的对象。它通过泛型提供类型安全,通过工厂方法简化创建,并通过清晰的访问方法(getLeft/getRight)提高代码可读性。它在需要临时组合两个值、从方法返回多个结果或在集合中存储关联对时非常有用。然而,对于具有重要业务含义的实体,定义专门的类通常是更优的选择。
