1. 介绍

MapUtils 是 Apache Commons Collections 项目中的一个工具类。

它提供了一些实用方法和修饰器,用于处理 java.util.Map 和 java.util.SortedMap 实例。

2. 配置

添加 mvn 依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.1</version>
</dependency>

3. 工具方法

3.1. 通过 Array 创建 Map

首先我们初始化一个一维数组、和一个二维数组:

public class MapUtilsTest {
    private String[][] color2DArray = new String[][] {
        {"RED", "#FF0000"},
        {"GREEN", "#00FF00"},
        {"BLUE", "#0000FF"}
    };
    private String[] color1DArray = new String[] {
        "RED", "#FF0000",
        "GREEN", "#00FF00",
        "BLUE", "#0000FF"
    };
    private Map<String, String> colorMap;

    //...
}

下面演示如何从一个二维数组中创建一个map:

@Test
public void whenCreateMapFrom2DArray_theMapIsCreated() {
    this.colorMap = MapUtils.putAll(
      new HashMap<>(), this.color2DArray);

    assertThat(
      this.colorMap, 
      is(aMapWithSize(this.color2DArray.length)));

    assertThat(this.colorMap, hasEntry("RED", "#FF0000"));
    assertThat(this.colorMap, hasEntry("GREEN", "#00FF00"));
    assertThat(this.colorMap, hasEntry("BLUE", "#0000FF"));
}

一维数组:

@Test
public void whenCreateMapFrom1DArray_theMapIsCreated() {
    this.colorMap = MapUtils.putAll(
      new HashMap<>(), this.color1DArray);

    assertThat(
      this.colorMap, 
      is(aMapWithSize(this.color1DArray.length / 2)));

    assertThat(this.colorMap, hasEntry("RED", "#FF0000"));
    assertThat(this.colorMap, hasEntry("GREEN", "#00FF00"));
    assertThat(this.colorMap, hasEntry("BLUE", "#0000FF"));
}

3.2. 打印 Map 内容

经常我们调试的时候,希望打印整个map:

@Test
public void whenVerbosePrintMap_thenMustPrintFormattedMap() {
    MapUtils.verbosePrint(System.out, "Optional Label", this.colorMap);
}

输出结果:

Optional Label = 
{
    RED = #FF0000
    BLUE = #0000FF
    GREEN = #00FF00
}

也可使用 *debugPrint()*,它可以额外地打印出数值的数据类型。

3.3. 取值

MapUtils 提供了一些 null-safe 的get方法。

例如,使用 getString() 获取 String 类型的值,String值是通过toString()获得的。如果值为null或者类型转换失败,可以指定返回默认值:

@Test
public void whenGetKeyNotPresent_thenMustReturnDefaultValue() {
    String defaultColorStr = "COLOR_NOT_FOUND";
    String color = MapUtils
      .getString(this.colorMap, "BLACK", defaultColorStr);

    assertEquals(color, defaultColorStr);
}

这些方法是 null 安全的,可以安全的处理null map:

@Test
public void whenGetOnNullMap_thenMustReturnDefaultValue() {
    String defaultColorStr = "COLOR_NOT_FOUND";
    String color = MapUtils.getString(null, "RED", defaultColorStr);

    assertEquals(color, defaultColorStr);
}

3.4. 反转 Map

我们还可以轻松地反转一个 map,即将 key和value进行调换:

@Test
public void whenInvertMap_thenMustReturnInvertedMap() {
    Map<String, String> invColorMap = MapUtils.invertMap(this.colorMap);

    int size = invColorMap.size();
    Assertions.assertThat(invColorMap)
      .hasSameSizeAs(colorMap)
      .containsKeys(this.colorMap.values().toArray(new String[] {}))
      .containsValues(this.colorMap.keySet().toArray(new String[] {}));
}

结果:

{
    #00FF00 = GREEN
    #FF0000 = RED
    #0000FF = BLUE
}

如果源 map 存在多个key关联相同的值,则在反转后,其中一个值将随机成为一个key。

3.5. 空值检查

isEmpty() 用于判断 Map 是否为null或元素为空。

safeAddToMap() 方法防止将 null 添加到 map 中

4. Map 修饰器

4.1. 固定大小 Map

fixedSizeMap() 会返回一个固定大小的map。可以更改,但无法新增或删除元素:

@Test(expected = IllegalArgumentException.class)
public void whenCreateFixedSizedMapAndAdd_thenMustThrowException() {
    Map<String, String> rgbMap = MapUtils
      .fixedSizeMap(MapUtils.putAll(new HashMap<>(), this.color1DArray));

    rgbMap.put("ORANGE", "#FFA500");
}

4.2. Predicated Map

predicatedMap() 方法返回一个 map,确保所有的元素都符合提供的 predicate:

@Test(expected = IllegalArgumentException.class)
public void whenAddDuplicate_thenThrowException() {
    Map<String, String> uniqValuesMap 
      = MapUtils.predicatedMap(this.colorMap, null, 
        PredicateUtils.uniquePredicate());

    uniqValuesMap.put("NEW_RED", "#FF0000");
}

上面,我们使用 PredicateUtils.uniquePredicate() ,任何尝试将重复值插入此 map 中都将抛出 IllegalArgumentException 异常。

我们也可实现 Predicate 接口来自定义异常。

4.3. Lazy Map

lazyMap() 返回一个 map,其中的值在被请求时才会被初始化。

如果传递给该 map 的 Map.get(Object) 方法的键在 map 中不存在,将使用 Transformer 实例来创建一个新对象,并与请求的键关联:

@Test
public void whenCreateLazyMap_theMapIsCreated() {
    Map<Integer, String> intStrMap = MapUtils.lazyMap(
      new HashMap<>(),
      TransformerUtils.stringValueTransformer());

    assertThat(intStrMap, is(anEmptyMap()));

    intStrMap.get(1);
    intStrMap.get(2);
    intStrMap.get(3);

    assertThat(intStrMap, is(aMapWithSize(3)));
}

5. 总结

本文我们探索了 Apache Commons Collections 的 MapUtils 类,并介绍了各种实用方法和修饰器,可以简化各种常见的 map 操作。

文中代码可在 GitHub 上找到。