1. 前言
本文介绍的ListUtils以org.apache.commons.collections4.MapUtils 为例
源代码:org.apache.commons.collections4.MapUtils
自定义特征可以在自己服务内实现
在Java开发中,Map是我们日常编程中使用最频繁的数据结构之一。无论是配置管理、缓存实现还是数据转换,Map都扮演着至关重要的角色。然而,面对复杂的业务场景,原生的Map操作往往显得力不从心:类型转换的繁琐、空指针的隐患、嵌套结构的处理等问题时常困扰着开发者。
Apache Commons Collections中的MapUtils正是为解决这些问题而生的全能工具。它就像一把精心打造的瑞士军刀,为Java开发者提供了丰富而强大的Map操作功能,让键值对处理变得简单、安全而高效。
想象一下这样的场景:你需要从复杂的配置Map中安全地提取各种类型的值,同时要处理可能的空值情况。传统方式需要大量的判空和类型转换代码,而使用MapUtils,一切变得如此简洁:
java
// 传统方式:繁琐的判空和类型转换
Integer timeout = null;
if (configMap != null && configMap.containsKey("timeout")) {
Object value = configMap.get("timeout");
if (value instanceof Integer) {
timeout = (Integer) value;
} else if (value instanceof String) {
try {
timeout = Integer.parseInt((String) value);
} catch (NumberFormatException e) {
timeout = 30; // 默认值
}
}
}
// 使用MapUtils:一行代码搞定
Integer timeout = MapUtils.getInteger(configMap, "timeout", 30);
在这里,MapUtils不仅简化了代码,更重要的是提供了健壮的类型安全和空值处理机制。
2. 常用方法
org.apache.commons.collections4.MapUtils 是 Apache Commons Collections 库中的一个实用工具类,专门为 java.util.Map 接口提供增强的静态方法。它包含了各种常用的映射操作,如安全类型获取、空值处理、装饰器模式应用等,能够显著简化映射处理的代码。
2.1 安全类型获取方法
2.1.1 方法签名
java
// 基础对象获取
static <K, V> V getObject(Map<? super K, V> map, K key)
static <K, V> V getObject(Map<K, V> map, K key, V defaultValue)
// 字符串类型获取
static <K> String getString(Map<? super K, ?> map, K key)
static <K> String getString(Map<? super K, ?> map, K key, String defaultValue)
// 数值类型获取
static <K> Number getNumber(Map<? super K, ?> map, K key)
static <K> Byte getByte(Map<? super K, ?> map, K key)
static <K> Integer getInteger(Map<? super K, ?> map, K key)
// ... 其他数值类型类似
// Map类型获取
static <K> Map<?, ?> getMap(Map<? super K, ?> map, K key)
2.1.2 应用示范
java
Map<String, Object> config = new HashMap<>();
config.put("timeout", "30");
config.put("enabled", "true");
config.put("max_connections", 100);
config.put("database", Map.of("url", "jdbc:mysql://localhost:3306/test"));
// 安全获取各种类型值
Integer timeout = MapUtils.getInteger(config, "timeout"); // 自动转换字符串"30"为Integer
Boolean enabled = MapUtils.getBoolean(config, "enabled"); // 自动转换字符串"true"为Boolean
Map<?, ?> dbConfig = MapUtils.getMap(config, "database"); // 安全获取嵌套Map
// 带默认值的获取
Integer maxThreads = MapUtils.getInteger(config, "max_threads", 10); // 键不存在返回默认值10
String host = MapUtils.getString(config, "host", "localhost");
2.1.3 关键源码解析
java
public static <K> Boolean getBoolean(Map<? super K, ?> map, K key) {
if (map != null) {
Object answer = map.get(key);
if (answer != null) {
// 支持多种Boolean表示形式
if (answer instanceof Boolean) {
return (Boolean)answer; // 直接返回Boolean类型
}
if (answer instanceof String) {
return Boolean.valueOf((String)answer); // 字符串转换
}
if (answer instanceof Number) {
// 数值类型:非0为true,0为false
Number n = (Number)answer;
return n.intValue() != 0 ? Boolean.TRUE : Boolean.FALSE;
}
}
}
return null; // 键不存在或值为null返回null
}
public static <K> Number getNumber(Map<? super K, ?> map, K key) {
if (map != null) {
Object answer = map.get(key);
if (answer != null) {
if (answer instanceof Number) {
return (Number)answer; // 直接返回Number类型
}
if (answer instanceof String) {
try {
// 字符串解析为Number
String text = (String)answer;
return NumberFormat.getInstance().parse(text);
} catch (ParseException var4) {
// 解析失败静默返回null
}
}
}
}
return null;
}
public static <K, V> V getObject(Map<? super K, V> map, K key) {
return (V)(map != null ? map.get(key) : null);
}
2.2 原始类型获取方法
2.2.1 方法签名
java
// 原始boolean类型获取
static <K> boolean getBooleanValue(Map<? super K, ?> map, K key)
static <K> boolean getBooleanValue(Map<? super K, ?> map, K key, boolean defaultValue)
// 原始数值类型获取
static <K> byte getByteValue(Map<? super K, ?> map, K key)
static <K> int getIntValue(Map<? super K, ?> map, K key)
static <K> long getLongValue(Map<? super K, ?> map, K key)
// ... 其他原始类型类似,都支持带默认值版本
2.2.2 应用示范
java
Map<String, Object> settings = new HashMap<>();
settings.put("retry_count", 3);
settings.put("cache_enabled", true);
// 获取原始类型值(避免NullPointerException)
int retryCount = MapUtils.getIntValue(settings, "retry_count"); // 3
boolean cacheEnabled = MapUtils.getBooleanValue(settings, "cache_enabled"); // true
// 键不存在时使用默认值
double timeout = MapUtils.getDoubleValue(settings, "timeout", 30.0); // 30.0
int maxSize = MapUtils.getIntValue(settings, "max_size", 100); // 100
2.2.3 关键源码解析
java
public static <K> boolean getBooleanValue(Map<? super K, ?> map, K key) {
// 使用Boolean.TRUE.equals()确保null安全
return Boolean.TRUE.equals(getBoolean(map, key));
}
public static <K> int getIntValue(Map<? super K, ?> map, K key) {
Integer integerObject = getInteger(map, key);
return integerObject == null ? 0 : integerObject; // null转换为0
}
public static <K> int getIntValue(Map<? super K, ?> map, K key, int defaultValue) {
Integer integerObject = getInteger(map, key);
// 支持自定义默认值
return integerObject == null ? defaultValue : integerObject;
}
2.3 Map装饰器方法
2.3.1 方法签名
java
// 同步装饰器
static <K, V> Map<K, V> synchronizedMap(Map<K, V> map)
static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> map)
// 不可修改装饰器
static <K, V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> map)
static <K, V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> map)
// 谓词验证装饰器
static <K, V> IterableMap<K, V> predicatedMap(Map<K, V> map, Predicate<? super K> keyPred, Predicate<? super V> valuePred)
// 转换装饰器
static <K, V> IterableMap<K, V> transformedMap(Map<K, V> map, Transformer<? super K, ? extends K> keyTransformer, Transformer<? super V, ? extends V> valueTransformer)
// 惰性加载装饰器
static <K, V> IterableMap<K, V> lazyMap(Map<K, V> map, Factory<? extends V> factory)
static <K, V> IterableMap<K, V> lazyMap(Map<K, V> map, Transformer<? super K, ? extends V> transformerFactory)
// 固定大小装饰器
static <K, V> IterableMap<K, V> fixedSizeMap(Map<K, V> map)
2.3.2 应用示范
java
Map<String, Object> originalMap = new HashMap<>();
// 线程安全包装
Map<String, Object> syncMap = MapUtils.synchronizedMap(originalMap);
// 只读包装
Map<String, Object> unmodifiableMap = MapUtils.unmodifiableMap(originalMap);
// 数据验证包装
Map<String, Integer> validatedMap = MapUtils.predicatedMap(
new HashMap<>(),
key -> key != null && key.startsWith("config_"), // 键必须以config_开头
value -> value != null && value > 0 // 值必须大于0
);
// 自动转换包装
Map<String, String> transformedMap = MapUtils.transformedMap(
new HashMap<>(),
key -> key.toLowerCase(), // 键自动转为小写
value -> value.trim() // 值自动去除空格
);
// 惰性加载Map
Map<String, List<String>> lazyMap = MapUtils.lazyMap(
new HashMap<>(),
() -> new ArrayList<>() // 访问不存在的键时自动创建ArrayList
);
List<String> list = lazyMap.get("users"); // 自动创建新的ArrayList
2.3.3 关键源码解析
java
// 同步装饰器 - 委托给Collections.synchronizedMap
public static <K, V> Map<K, V> synchronizedMap(Map<K, V> map) {
return Collections.synchronizedMap(map); // 直接调用Java标准库的同步包装
}
// 不可修改装饰器 - 委托给对应的Unmodifiable装饰器类
public static <K, V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> map) {
return UnmodifiableMap.unmodifiableMap(map); // 调用UnmodifiableMap的工厂方法
}
// 谓词验证装饰器 - 委托给PredicatedMap
public static <K, V> IterableMap<K, V> predicatedMap(Map<K, V> map,
Predicate<? super K> keyPred,
Predicate<? super V> valuePred) {
return PredicatedMap.predicatedMap(map, keyPred, valuePred); // 调用PredicatedMap的工厂方法
}
// 转换装饰器 - 委托给TransformedMap
public static <K, V> IterableMap<K, V> transformedMap(Map<K, V> map,
Transformer<? super K, ? extends K> keyTransformer,
Transformer<? super V, ? extends V> valueTransformer) {
return TransformedMap.transformingMap(map, keyTransformer, valueTransformer); // 调用TransformedMap的工厂方法
}
// 惰性加载装饰器 - 委托给LazyMap
public static <K, V> IterableMap<K, V> lazyMap(Map<K, V> map, Factory<? extends V> factory) {
return LazyMap.lazyMap(map, factory); // 使用Factory的惰性Map
}
public static <K, V> IterableMap<K, V> lazyMap(Map<K, V> map, Transformer<? super K, ? extends V> transformerFactory) {
return LazyMap.lazyMap(map, transformerFactory); // 使用Transformer的惰性Map
}
// 固定大小装饰器 - 委托给FixedSizeMap
public static <K, V> IterableMap<K, V> fixedSizeMap(Map<K, V> map) {
return FixedSizeMap.fixedSizeMap(map); // 调用FixedSizeMap的工厂方法
}
2.4 工具方法
2.4.1 方法签名
java
// 空值安全处理
static <K, V> Map<K, V> emptyIfNull(Map<K, V> map)
static boolean isEmpty(Map<?, ?> map)
static boolean isNotEmpty(Map<?, ?> map)
// Map转换
static <K, V> Properties toProperties(Map<K, V> map)
static Map<String, Object> toMap(ResourceBundle resourceBundle)
// Map反转
static <K, V> Map<V, K> invertMap(Map<K, V> map)
// 数据填充
static <K, V> void populateMap(Map<K, V> map, Iterable<? extends V> elements, Transformer<V, K> keyTransformer)
static <K, V> void populateMap(MultiMap<K, V> map, Iterable<? extends E> elements, Transformer<E, K> keyTransformer, Transformer<E, V> valueTransformer)
// 调试输出
static void verbosePrint(PrintStream out, Object label, Map<?, ?> map)
static void debugPrint(PrintStream out, Object label, Map<?, ?> map)
2.4.2 应用示范
java
// 空值安全处理示例
Map<String, String> possiblyNullMap = getMaybeNullMap();
Map<String, String> safeMap = MapUtils.emptyIfNull(possiblyNullMap); // 如果null则返回空Map
Map<String, Integer> config = new HashMap<>();
boolean empty = MapUtils.isEmpty(config); // true - Map为空
boolean notEmpty = MapUtils.isNotEmpty(config); // false
config.put("timeout", 30);
empty = MapUtils.isEmpty(config); // false
notEmpty = MapUtils.isNotEmpty(config); // true
// Map转换示例
Map<String, String> configMap = new HashMap<>();
configMap.put("db.host", "localhost");
configMap.put("db.port", "3306");
Properties props = MapUtils.toProperties(configMap); // 转换为Properties
ResourceBundle bundle = ResourceBundle.getBundle("messages");
Map<String, Object> bundleMap = MapUtils.toMap(bundle); // ResourceBundle转Map
// Map反转示例
Map<Integer, String> idToName = new HashMap<>();
idToName.put(1, "Alice");
idToName.put(2, "Bob");
Map<String, Integer> nameToId = MapUtils.invertMap(idToName); // 键值互换
// 数据填充示例
List<User> users = Arrays.asList(
new User("Alice", 25),
new User("Bob", 30)
);
Map<String, Integer> userAgeMap = new HashMap<>();
MapUtils.populateMap(userAgeMap, users, User::getName, User::getAge);
// 调试输出示例
Map<String, Object> complexMap = new HashMap<>();
complexMap.put("server", Map.of("host", "localhost", "port", 8080));
complexMap.put("database", Map.of("url", "jdbc:mysql://localhost:3306/test"));
MapUtils.verbosePrint(System.out, "Configuration", complexMap);
// 预期输出如下:
// Configuration = {
// server = {
// host = localhost
// port = 8080
// }
// database = {
// url = jdbc:mysql://localhost:3306/test
//}
2.4.3 关键源码解析
java
public static <K, V> Map<K, V> emptyIfNull(Map<K, V> map) {
return map == null ? Collections.emptyMap() : map;
}
public static boolean isEmpty(Map<?, ?> map) {
return map == null || map.isEmpty();
}
public static <K, V> Map<V, K> invertMap(Map<K, V> map) {
Map<V, K> out = new HashMap(map.size());
// 简单遍历实现键值反转
for(Map.Entry<K, V> entry : map.entrySet()) {
out.put(entry.getValue(), entry.getKey());
}
return out;
}
// 复杂的调试输出实现
private static void verbosePrintInternal(PrintStream out, Object label, Map<?, ?> map,
Deque<Map<?, ?>> lineage, boolean debug) {
// 处理嵌套Map的递归打印
if (map == null) {
printIndent(out, lineage.size());
out.println(label + " = null");
} else {
// 打印缩进和标签
printIndent(out, lineage.size());
out.println(label + " = {");
lineage.addLast(map); // 记录 lineage 避免循环引用
// 递归处理每个条目
for(Map.Entry<?, ?> entry : map.entrySet()) {
Object childValue = entry.getValue();
if (childValue instanceof Map && !lineage.contains(childValue)) {
// 递归打印子Map
verbosePrintInternal(out, entry.getKey(), (Map)childValue, lineage, debug);
} else {
// 打印普通值
printIndent(out, lineage.size());
out.print(entry.getKey());
out.print(" = ");
// 处理循环引用检测
int lineageIndex = IterableUtils.indexOf(lineage, PredicateUtils.equalPredicate(childValue));
if (lineageIndex == -1) {
out.print(childValue);
} else {
out.print("(ancestor[" + (lineage.size() - 1 - lineageIndex - 1) + "] Map)");
}
out.println();
}
}
lineage.removeLast();
printIndent(out, lineage.size());
out.println("}");
}
}
2.5 迭代器包装方法
2.5.1 方法签名
java
// 包装为可迭代Map
static <K, V> IterableMap<K, V> iterableMap(Map<K, V> map)
// 包装为可迭代SortedMap
static <K, V> IterableSortedMap<K, V> iterableSortedMap(SortedMap<K, V> sortedMap)
2.5.2 应用示范
java
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
// 包装为可迭代Map,支持增强的迭代功能
IterableMap<String, Integer> iterableMap = MapUtils.iterableMap(map);
// 使用MapIterator进行迭代(比entrySet更高效)
MapIterator<String, Integer> it = iterableMap.mapIterator();
while (it.hasNext()) {
it.next(); // 移动到下一个元素
String key = it.getKey();
Integer value = it.getValue();
System.out.println(key + " = " + value);
}
2.5.3 关键源码解析
java
public static <K, V> IterableMap<K, V> iterableMap(Map<K, V> map) {
if (map == null) {
throw new NullPointerException("Map must not be null");
}
// 如果已经是IterableMap直接返回,否则使用装饰器包装
return (IterableMap<K, V>)(map instanceof IterableMap ?
(IterableMap)map : new AbstractMapDecorator<K, V>(map) {});
}
3. MapUtils 与 Stream API 的对比
3.1 设计哲学差异
java
// MapUtils:命令式、工具类思维
Map<String, Integer> result = new HashMap<>();
if (MapUtils.isNotEmpty(sourceMap)) {
for (Map.Entry<String, Object> entry : sourceMap.entrySet()) {
Integer value = MapUtils.getInteger(entry.getValue());
if (value != null && value > 0) {
result.put(entry.getKey().toUpperCase(), value);
}
}
}
// Stream API:声明式、函数式思维
Map<String, Integer> result = sourceMap.entrySet().stream()
.filter(entry -> entry.getValue() instanceof Integer)
.map(entry -> Map.entry(
entry.getKey().toUpperCase(),
(Integer) entry.getValue()
))
.filter(entry -> entry.getValue() > 0)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue
));
3.2 类型安全操作对比
java
// MapUtils:类型安全的便捷方法
Integer timeout = MapUtils.getInteger(config, "timeout", 30);
Boolean enabled = MapUtils.getBoolean(config, "enabled", false);
List<String> items = (List<String>) MapUtils.getObject(config, "items");
// Stream API:需要手动类型检查和转换
Integer timeout = Optional.ofNullable(config.get("timeout"))
.filter(Integer.class::isInstance)
.map(Integer.class::cast)
.orElse(30);
Boolean enabled = Optional.ofNullable(config.get("enabled"))
.map(obj -> {
if (obj instanceof Boolean) return (Boolean) obj;
if (obj instanceof String) return Boolean.parseBoolean((String) obj);
return false;
})
.orElse(false);
3.3 复杂数据转换对比
java
// MapUtils:适合简单的单步操作
Map<String, String> upperCaseMap = MapUtils.transformedMap(
originalMap,
String::toUpperCase,
String::toUpperCase
);
// Stream API:适合复杂的数据流水线
Map<String, String> processedMap = originalMap.entrySet().stream()
.filter(entry -> entry.getKey().startsWith("config_"))
.map(entry -> Map.entry(
entry.getKey().toUpperCase().replace("CONFIG_", ""),
entry.getValue().trim().toUpperCase()
))
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1, // 合并函数
LinkedHashMap::new // 保持顺序
));
3.4 性能考虑对比
java
// MapUtils:对于简单操作通常更高效
public void processLargeMap(Map<String, Object> largeMap) {
// 直接操作,开销小
if (MapUtils.isNotEmpty(largeMap)) {
for (Map.Entry<String, Object> entry : largeMap.entrySet()) {
processItem(entry.getKey(), entry.getValue());
}
}
}
// Stream API:对于复杂流水线和并行处理有优势
public void processLargeMapParallel(Map<String, Object> largeMap) {
largeMap.entrySet().parallelStream()
.filter(entry -> isValid(entry.getKey()))
.map(this::transformEntry)
.forEach(this::processTransformed);
}
4. 总结
MapUtils与Stream API在Map处理领域呈现出不同的设计哲学和适用边界:
- MapUtils立足于数据访问的安全性,通过类型安全的获取方法和装饰器模式,为键值操作提供了"防错"机制,特别适合配置管理、数据验证等需要强健性的场景;
- Stream API着眼于数据转换的流畅性,借助函数式编程范式构建声明式的处理流水线,在复杂数据转换和并行计算方面展现出独特优势。
这种差异本质上是专用工具与通用范式的区别:MapUtils如同精心打造的手术刀,针对Map操作的痛点提供精准解决方案;Stream API则像多功能工具箱,通过基础组件的灵活组合应对各种复杂情况。
在实际技术选型中,有如下建议:对于结构相对固定、以读取为主的配置数据,MapUtils的类型安全性和简洁API更具价值;而对于需要复杂转换、过滤和聚合的动态数据流,Stream API的声明式风格更能体现其优势。
参考资料: