如何在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");
        }
    };
}

当前回答

我更喜欢使用静态初始化式来避免生成匿名类(这将没有进一步的用途),因此我将列出使用静态初始化式进行初始化的技巧。所有列出的解决方案/提示都是类型安全的。

注意:这个问题没有说任何关于使地图不可修改的问题,所以我将把它省略,但要知道可以很容易地使用Collections.unmodifiableMap(map)来完成。

第一个提示

第一个技巧是,你可以对地图进行局部引用,并给它一个简短的名字:

private static final Map<Integer, String> myMap = new HashMap<>();
static {
    final Map<Integer, String> m = myMap; // Use short name!
    m.put(1, "one"); // Here referencing the local variable which is also faster!
    m.put(2, "two");
    m.put(3, "three");
}

第二个提示

第二个技巧是,您可以创建一个helper方法来添加条目;你也可以将这个helper方法设为public,如果你想:

private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
    p(1, "one"); // Calling the helper method.
    p(2, "two");
    p(3, "three");
}

private static void p(Integer k, String v) {
    myMap2.put(k, v);
}

但是这里的helper方法是不可重用的,因为它只能向myMap2添加元素。为了使它可重用,我们可以使映射本身成为helper方法的参数,但这样初始化代码就不会更短了。

第三个技巧

第三个技巧是,您可以创建一个具有填充功能的可重用的类似生成器的helper类。这是一个简单的,10行类型安全的helper类:

public class Test {
    private static final Map<Integer, String> myMap3 = new HashMap<>();
    static {
        new B<>(myMap3)   // Instantiating the helper class with our map
            .p(1, "one")
            .p(2, "two")
            .p(3, "three");
    }
}

class B<K, V> {
    private final Map<K, V> m;

    public B(Map<K, V> m) {
        this.m = m;
    }

    public B<K, V> p(K k, V v) {
        m.put(k, v);
        return this; // Return this for chaining
    }
}

其他回答

我喜欢使用静态初始化“技术”,当我有一个抽象类的具体实现,它定义了一个初始化构造函数,但没有默认构造函数,但我希望我的子类有一个默认构造函数。

例如:

public abstract class Shape {

    public static final String COLOR_KEY = "color_key";
    public static final String OPAQUE_KEY = "opaque_key";

    private final String color;
    private final Boolean opaque;

    /**
     * Initializing constructor - note no default constructor.
     *
     * @param properties a collection of Shape properties
     */
    public Shape(Map<String, Object> properties) {
        color = ((String) properties.getOrDefault(COLOR_KEY, "black"));
        opaque = (Boolean) properties.getOrDefault(OPAQUE_KEY, false);
    }

    /**
     * Color property accessor method.
     *
     * @return the color of this Shape
     */
    public String getColor() {
        return color;
    }

    /**
     * Opaque property accessor method.
     *
     * @return true if this Shape is opaque, false otherwise
     */
    public Boolean isOpaque() {
        return opaque;
    }
}

以及这个类的具体实现——但它想要/需要一个默认构造函数:

public class SquareShapeImpl extends Shape {

    private static final Map<String, Object> DEFAULT_PROPS = new HashMap<>();

    static {
        DEFAULT_PROPS.put(Shape.COLOR_KEY, "yellow");
        DEFAULT_PROPS.put(Shape.OPAQUE_KEY, false);
    }

    /**
     * Default constructor -- intializes this square to be a translucent yellow
     */
    public SquareShapeImpl() {
        // the static initializer was useful here because the call to 
        // this(...) must be the first statement in this constructor
        // i.e., we can't be mucking around and creating a map here
        this(DEFAULT_PROPS);
    }

    /**
     * Initializing constructor -- create a Square with the given
     * collection of properties.
     *
     * @param props a collection of properties for this SquareShapeImpl
     */
    public SquareShapeImpl(Map<String, Object> props) {
        super(props);
    }
}

然后要使用这个默认构造函数,只需执行以下操作:

public class StaticInitDemo {

    public static void main(String[] args) {

        // create a translucent, yellow square...
        Shape defaultSquare = new SquareShapeImpl();

        // etc...
    }
}

第二个方法的一个优点是,你可以用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!

使用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的提交者

我还没有在任何答案中看到我使用的方法(并且已经逐渐喜欢上了),所以这里是:

我不喜欢使用静态初始化器,因为它们很笨拙, 我不喜欢匿名类,因为它为每个实例创建一个新类。

相反,我更喜欢这样的初始化:

map(
    entry("keyA", "val1"),
    entry("keyB", "val2"),
    entry("keyC", "val3")
);

不幸的是,这些方法不是标准Java库的一部分, 所以你需要创建(或使用)一个实用程序库,它定义了以下方法:

 public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
 public static <K,V> Map.Entry<K,V> entry(K key, V val)

(你可以使用'import static'来避免在方法名前加上前缀)

我发现为其他集合(list、set、sortedSet、sortedMap等)提供类似的静态方法很有用。

它不像json对象初始化那样好,但就可读性而言,这是朝着那个方向迈出的一步。

如果你只需要向映射中添加一个值,你可以使用Collections.singletonMap:

Map<K, V> map = Collections.singletonMap(key, value)