如何在Java中初始化一个静态Map ?

方法一:静态初始化器 方法二:实例初始化器(匿名子类) 或 还有别的方法吗?

它们各自的优点和缺点是什么?

下面是一个例子来说明这两种方法:

import java.util.HashMap;
import java.util.Map;

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}

当前回答

下面是一个Java 8单行静态映射初始化器:

private static final Map<String, String> EXTENSION_TO_MIMETYPE =
    Arrays.stream(new String[][] {
        { "txt", "text/plain" }, 
        { "html", "text/html" }, 
        { "js", "application/javascript" },
        { "css", "text/css" },
        { "xml", "application/xml" },
        { "png", "image/png" }, 
        { "gif", "image/gif" }, 
        { "jpg", "image/jpeg" },
        { "jpeg", "image/jpeg" }, 
        { "svg", "image/svg+xml" },
    }).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));

编辑:要初始化一个Map<Integer, String>,就像问题中那样,你需要这样的东西:

static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
        {1, "one"},
        {2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));

编辑(2):i_am_zero有一个更好的、支持混合类型的版本,它使用新的SimpleEntry<>(k, v)调用流。看看答案:https://stackoverflow.com/a/37384773/3950982

其他回答

第二个方法可以在需要时调用受保护的方法。这对于初始化构造后不可变的类很有用。

在这种情况下,我绝不会创建匿名子类。静态初始化器同样有效,如果你想让映射不可修改,例如:

private static final Map<Integer, String> MY_MAP;
static
{
    Map<Integer, String>tempMap = new HashMap<Integer, String>();
    tempMap.put(1, "one");
    tempMap.put(2, "two");
    MY_MAP = Collections.unmodifiableMap(tempMap);
}

因为Java不支持地图文字,所以必须始终显式地实例化和填充地图实例。

幸运的是,在Java中可以使用工厂方法来近似映射字面量的行为。

例如:

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

输出:

{a=1, b=2, c=3}

这比一次创建和填充一个元素要方便得多。

实例初始化器在这里只是语法糖,对吧?我不明白为什么需要一个额外的匿名类来初始化。如果创建的类是final类,那么它将不起作用。

你也可以使用静态初始化器创建一个不可变映射:

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

即使使用Guava很好的ImmutableMap类,有时我也想流畅地构建一个可变映射。发现自己想要避免静态块和匿名子类型的东西,当Java 8出现时,我写了一个小库来帮助我,叫做Fluent。

// simple usage, assuming someMap is a Map<String, String> already declared
Map<String, String> example = new Fluent.HashMap<String, String>()
    .append("key1", "val1")
    .append("key2", "val2")
    .appendAll(someMap);

与Java 8接口默认我可以实现Fluent。所有标准Java Map实现的Map方法(例如HashMap, ConcurrentSkipListMap,…)等等),没有乏味的重复。

不可修改的地图也很简单。

Map<String, Integer> immutable = new Fluent.LinkedHashMap<String, Integer>()
    .append("one", 1)
    .append("two", 2)
    .append("three", 3)
    .unmodifiable();

参见https://github.com/alexheretic/fluent获取源代码、文档和示例。