如何初始化静态Map?

1241

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

2
在Java 8中初始化一个Map:https://dev59.com/questions/1nRB5IYBdhLWcg3wyqOo#37384773 - akhil_mittal
2
请不要使用双括号初始化(double brace initialization) - 这是一种hack方法,容易导致内存泄漏和其他问题。 - dimo414
Java 9?如果输入数量<=10,请使用Map.of,否则请使用Map.ofEntries,请参见https://dev59.com/1nRB5IYBdhLWcg3wyqOo#37384773。 - akhil_mittal
43个回答

19

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

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

18

也许看看Google Collections会很有趣,例如他们页面上的视频。它们提供了各种初始化地图和集合的方法,并提供不可变的集合。

更新:此库现已更名为Guava


12
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作为常量,否则它就不能被视为常量。


10
我强烈建议采用“双括号初始化”风格而不是静态块风格。
有人可能会评论它们不喜欢匿名类、开销、性能等问题。
但我更加考虑的是代码可读性和可维护性。从这个角度来看,我认为双括号比静态方法更好的编码风格。
1. 元素嵌套且内联。 2. 更面向对象,而不是过程式的。 3. 性能影响非常小,可以忽略。 4. 更好的IDE大纲支持(而不是许多匿名static{}块)。 5. 您保存了几行注释来带来它们之间的关系。 6. 防止未初始化对象的元素泄漏/实例引导异常和字节码优化器。 7. 不必担心静态块的执行顺序。
此外,如果您意识到匿名类的GC,可以使用 new HashMap(Map map)将其转换为普通HashMap。
直到你遇到另一个问题才这样做。如果发生这种情况,您应该使用完全不同的编码风格(例如,没有静态,工厂类)。

9

像往常一样,apache-commons有一个适当的方法MapUtils.putAll(Map, Object[])

例如,要创建一个颜色映射:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

@mikerodent 4.1版本是通用的:public static <K,V> Map<K,V> putAll(final Map<K,V> map,final Object[] array) - agad
@mikerodent 这不是因为 Object[][] 吗?看一下更新后的回答 - 我在 Eclipse 中没有任何警告。 - agad
多奇怪啊...即使我使用String[][],我还是收到了“警告”!当然,这仅适用于您的KV是相同类的情况。我猜您(可以理解地)没有在Eclipse设置中将“未经检查的转换”设置为“忽略”? - mike rodent
@mikerodent 我没有注解,并且“未经检查的通用类型操作”设置为警告。从HashMap中删除类型会导致警告出现。 - agad
@mikerodent 我有Neon Release(4.6.0)和jdk1.8.0_74。 - agad
显示剩余3条评论

8

这是我最喜欢的方式,如果:

  • 我不想(或无法)使用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));

7
如果你想要一个不可修改的映射(map),Java 9最终添加了一个很棒的工厂方法ofMap接口中。类似的方法也被添加到了Set、List中。 Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");

7
我喜欢使用静态初始化器来避免产生没有任何进一步用途的匿名类,因此我将列出使用静态初始化器进行初始化的提示。所有列出的解决方案/提示都是类型安全的。
注意:问题没有说要使地图不可修改,因此我会将其留空,但请知道可以使用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");
}

第二个提示

第二个提示是你可以创建一个帮助方法来添加条目;如果需要,你还可以将此帮助方法声明为公共的:

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

这里的帮助方法是不可重复使用的,因为它只能向myMap2添加元素。为了使它可重复使用,我们可以将地图本身作为帮助方法的参数,但初始化代码将不会更短。

第三个提示

第三个提示是,您可以创建一个可重复使用的类似生成器的辅助类,具有填充功能。这实际上是一个简单的、有类型安全的10行辅助类:

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

5
你正在创建的匿名类很好用。但是你应该意识到这是一个内部类,因此它将包含对周围类实例的引用。所以你会发现你不能使用它来做某些事情(比如使用 XStream)。你会得到一些非常奇怪的错误。
话虽如此,只要你知道了这一点,这种方法就很好用了。我大多数时候都用它来初始化各种集合。
编辑:在评论中正确指出,这是一个静态类。显然我没有仔细阅读。但是我的评论仍然适用于匿名内部类。

3
在这种特定情况下,它是静态的,因此没有外部实例。 - Tom Hawtin - tackline
可以说,XStream 不应该尝试序列化这样的东西(它是静态的。为什么需要序列化静态变量呢?) - jasonmp85

5

如果你想要简洁且相对安全的方法,可以将编译时的类型检查转移到运行时:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接