如何在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.of() 或者我需要一个可变Map 或者我需要从JDK9+的Map.of()中超过10个条目限制

public static <A> Map<String, A> asMap(Object... keysAndValues) {
  return new LinkedHashMap<String, A>() {{
    for (int i = 0; i < keysAndValues.length - 1; i++) {
      put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
    }
  }};
}

它非常紧凑,并且忽略了杂散值(即没有值的最终键)。

用法:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));

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

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

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

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

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

我们可以用地图。ofEntries,调用Map。条目(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"));

我们也可以使用Map。如Tagir在他的回答中所建议的,但我们不能使用Map.of有超过10个条目。

Java 8

我们可以创建一个映射条目流。在java.util.AbstractMap中我们已经有两个Entry的实现,它们是SimpleEntry和SimpleImmutableEntry。在这个例子中,我们可以使用former as:

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

实例初始化器在这里只是语法糖,对吧?我不明白为什么需要一个额外的匿名类来初始化。如果创建的类是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);
    }
}