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

当前回答

注意:这个答案实际上属于问题如何直接初始化一个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}

其他回答

即使使用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获取源代码、文档和示例。

如果你想要一些简洁和相对安全的东西,你可以将编译时类型检查转移到运行时:

static final Map<String, Integer> map = MapUtils.unmodifiableMap(
    String.class, Integer.class,
    "cat",  4,
    "dog",  2,
    "frog", 17
);

这个实现应该捕获任何错误:

import java.util.HashMap;

public abstract class MapUtils
{
    private MapUtils() { }

    public static <K, V> HashMap<K, V> unmodifiableMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        return Collections.<K, V>unmodifiableMap(makeMap(
            keyClazz,
            valClazz,
            keyValues));
    }

    public static <K, V> HashMap<K, V> makeMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        if (keyValues.length % 2 != 0)
        {
            throw new IllegalArgumentException(
                    "'keyValues' was formatted incorrectly!  "
                  + "(Expected an even length, but found '" + keyValues.length + "')");
        }

        HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);

        for (int i = 0; i < keyValues.length;)
        {
            K key = cast(keyClazz, keyValues[i], i);
            ++i;
            V val = cast(valClazz, keyValues[i], i);
            ++i;
            result.put(key, val);
        }

        return result;
    }

    private static <T> T cast(Class<? extends T> clazz, Object object, int i)
    {
        try
        {
            return clazz.cast(object);
        }
        catch (ClassCastException e)
        {
            String objectName = (i % 2 == 0) ? "Key" : "Value";
            String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
            throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
        }
    }
}

这一个使用Apache common -lang,它很可能已经在你的类路径上了:

Map<String, String> collect = Stream.of(
        Pair.of("hello", "world"),
        Pair.of("abc", "123"),
        Pair.of("java", "eight")
).collect(Collectors.toMap(Pair::getKey, Pair::getValue));

我喜欢匿名类,因为它很容易处理:

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});

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

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