如何初始化静态地图?

您将如何用 Java 初始化静态Map

方法一:静态初始化
方法二:实例初始化程序(匿名子类)还是其他方法?

各自的优缺点是什么?

这是说明两种方法的示例:

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

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

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

答案

在这种情况下,实例初始化器只是语法糖,对吗?我不明白为什么您只需要一个额外的匿名类即可进行初始化。如果正在创建的类是最终的,那将是行不通的。

您也可以使用静态初始化程序创建不可变映射:

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初始化静态,不变地图的方法:

static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);

如您所见,它非常简洁(由于ImmutableMap便利的工厂方法)。

如果希望地图具有 5 个以上的条目,则不能再使用ImmutableMap.of() 。相反,请按照以下方式尝试ImmutableMap.builder()

static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();

要了解更多有关的番石榴的不可变集合实用工具的好处,看到番石榴用户手册的说明不可变的集合

番石榴(的一部分)曾经被称为Google Collections 。如果您尚未在 Java 项目中使用此库, 强烈建议您尝试一下!正如其他 SO 用户所同意的那样,Guava 已迅速成为 Java 最受欢迎和最有用的免费第三方库之一。 (如果您不熟悉它,则该链接后面有一些出色的学习资源。)


更新(2015 年) :关于Java 8 ,我仍然会使用 Guava 方法,因为它比其他任何方法都更干净。如果您不希望 Guava 依赖,请考虑使用普通的 init 方法 。如果您问我,使用二维数组和 Stream API 的骇客是非常丑陋的,如果您需要创建一个键和值不是同一类型的Map<Integer, String> (例如问题中的Map<Integer, String> ),就变得很丑陋。

至于 Guava 的未来,就 Java 8 而言,Louis Wasserman 曾在 2014 年和 2016 年说过 [ 更新 ],它宣布Guava 21 将需要并适当地支持 Java 8


更新(2016 年) :正如Tagir Valeev 所指出的那样Java 9最终将通过为集合添加便捷的工厂方法来使此干净,仅使用纯 JDK 即可完成:

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);

Java 5 提供了以下更紧凑的语法:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

我会用:

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<Integer, String>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
  1. 它避免了匿名类,我个人认为这是一种不好的风格,并且避免
  2. 它使地图的创建更加明确
  3. 它使地图无法修改
  4. 因为 MY_MAP 是常数,所以我将其命名为常数

第二种方法的一个优点是,您可以使用Collections.unmodifiableMap()将其包装起来,以确保以后没有任何东西可以更新集合:

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

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!

这是 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]));

Edit(2):i_am_zero 有一个更好的,具有混合类型的版本,它使用new SimpleEntry<>(k, v)调用流。查看该答案: https : //stackoverflow.com/a/37384773/3950982

Java 9 + 中的Map.of

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

有关详细信息,请参见JEP 269 。 JDK 9 于 2017 年 9 月全面上市

Java 9

我们可以使用Map.ofEntries ,调用Map.entry( k , v )创建每个条目。

import static java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
        entry(1, "one"),
        entry(2, "two"),
        entry(3, "three"),
        entry(4, "four"),
        entry(5, "five"),
        entry(6, "six"),
        entry(7, "seven"),
        entry(8, "eight"),
        entry(9, "nine"),
        entry(10, "ten"));

我们也可以按照 Tagir 在此处的回答中的建议使用Map.of ,但使用Map.of最多不能超过 10 个条目。

Java 8(整洁的解决方案)

我们可以创建一个地图条目流。我们已经在java.util.AbstractMap实现Entry两个实现,分别是SimpleEntrySimpleImmutableEntry 。对于此示例,我们可以将前者用作:

import java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
            new SimpleEntry<>(1, "one"),
            new SimpleEntry<>(2, "two"),
            new SimpleEntry<>(3, "three"),
            new SimpleEntry<>(4, "four"),
            new SimpleEntry<>(5, "five"),
            new SimpleEntry<>(6, "six"),
            new SimpleEntry<>(7, "seven"),
            new SimpleEntry<>(8, "eight"),
            new SimpleEntry<>(9, "nine"),
            new SimpleEntry<>(10, "ten"))
            .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));

使用Eclipse Collections ,以下所有功能均可使用:

import java.util.Map;

import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Maps;

public class StaticMapsTest
{
    private static final Map<Integer, String> MAP =
        Maps.mutable.with(1, "one", 2, "two");

    private static final MutableMap<Integer, String> MUTABLE_MAP =
       Maps.mutable.with(1, "one", 2, "two");


    private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();


    private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
        Maps.mutable.with(1, "one", 2, "two").asSynchronized();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").toImmutable();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
        Maps.immutable.with(1, "one", 2, "two");
}

您还可以使用 Eclipse Collections 静态初始化基本映射。

import org.eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps;

public class StaticPrimitiveMapsTest
{
    private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two");

    private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asUnmodifiable();

    private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asSynchronized();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .toImmutable();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
            IntObjectMaps.immutable.<String>empty()
                    .newWithKeyValue(1, "one")
                    .newWithKeyValue(2, "two");
}

注意:我是 Eclipse Collections 的提交者

在这种情况下,我永远不会创建匿名子类。如果您想使映射不可修改,则静态初始化器同样可以很好地工作,例如:

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);
}