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

当前回答

JEP 269为Collections API提供了一些方便的工厂方法。该工厂方法不在当前的Java版本(即8)中,但计划在Java 9发行版中使用。

对于Map有两个工厂方法:of和ofEntries。使用of,可以交替传递键/值对。例如,为了创建一个像{age: 27, major: cs}这样的Map:

Map<String, Object> info = Map.of("age", 27, "major", "cs");

目前of有十个重载版本,因此您可以创建一个包含十个键/值对的映射。如果你不喜欢这个限制或交替键/值,你可以使用ofEntries:

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

of和ofEntries都将返回一个不可变的Map,因此在构造之后不能更改它们的元素。您可以使用JDK 9 Early Access来尝试这些特性。

其他回答

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

注意:这个问题没有说任何关于使地图不可修改的问题,所以我将把它省略,但要知道可以很容易地使用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
    }
}

在Java 8中,过程方法也可以包装在Supplier中:

Map<String,String> m = ((Supplier<Map<String,String>>)(() -> {
    Map<String,String> result = new HashMap<>();
    result.put("foo","hoo");
    ...
    return result;
)).get();

这只是一种假设的方法,但如果您确实需要一行代码,则可以派上用场。

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

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

如果我们声明了多个常量,那么代码将以静态块形式编写,并且以后很难维护。所以最好使用匿名类。

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}

并且建议对常量使用unmodifiableMap,否则它不能被视为常量。

注意:这个答案实际上属于问题如何直接初始化一个HashMap(在字面上)?但由于在写这篇文章时,它被标记为这篇文章的副本……


在Java 9的Map.of()之前(它也被限制为10个映射),你可以扩展你选择的Map实现,例如:

public class InitHashMap<K, V> extends HashMap<K, V>

重新实现HashMap的构造函数:

public InitHashMap() {
    super();
}

public InitHashMap( int initialCapacity, float loadFactor ) {
    super( initialCapacity, loadFactor );
}

public InitHashMap( int initialCapacity ) {
    super( initialCapacity );
}

public InitHashMap( Map<? extends K, ? extends V> map ) {
    super( map );
}

并添加一个额外的构造函数,它受到Aerthel的答案的启发,但通过使用Object…和<K, V>类型:

public InitHashMap( final Object... keyValuePairs ) {

    if ( keyValuePairs.length % 2 != 0 )
        throw new IllegalArgumentException( "Uneven number of arguments." );

    K key = null;
    int i = -1;

    for ( final Object keyOrValue : keyValuePairs )
        switch ( ++i % 2 ) {
            case 0:  // key
                if ( keyOrValue == null )
                    throw new IllegalArgumentException( "Key[" + (i >>> 1) + "] is <null>." );
                key = (K) keyOrValue;
                continue;
            case 1:  // value
                put( key, (V) keyOrValue );
        }
}

Run

public static void main( final String[] args ) {

    final Map<Integer, String> map = new InitHashMap<>( 1, "First", 2, "Second", 3, "Third" );
    System.out.println( map );
}

输出

{1=First, 2=Second, 3=Third}

你也可以扩展Map接口:

public interface InitMap<K, V> extends Map<K, V> {

    static <K, V> Map<K, V> of( final Object... keyValuePairs ) {

        if ( keyValuePairs.length % 2 != 0 )
            throw new IllegalArgumentException( "Uneven number of arguments." );

        final Map<K, V> map = new HashMap<>( keyValuePairs.length >>> 1, .75f );
        K key = null;
        int i = -1;

        for ( final Object keyOrValue : keyValuePairs )
            switch ( ++i % 2 ) {
                case 0: // key
                    if ( keyOrValue == null )
                        throw new IllegalArgumentException( "Key[" + (i >>> 1) + "] is <null>." );
                    key = (K) keyOrValue;
                    continue;
                case 1: // value
                    map.put( key, (V) keyOrValue );
            }
        return map;
    }
}

Run

public static void main( final String[] args ) {

    System.out.println( InitMap.of( 1, "First", 2, "Second", 3, "Third" ) );
}

输出

{1=First, 2=Second, 3=Third}