如何初始化静态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个回答

4

您可以使用 StickyMapMapEntry 来自 Cactoos

private static final Map<String, String> MAP = new StickyMap<>(
  new MapEntry<>("name", "Jeffrey"),
  new MapEntry<>("age", "35")
);

4

在Java 8中,我开始使用以下模式:

private static final Map<String, Integer> MAP = Stream.of(
    new AbstractMap.SimpleImmutableEntry<>("key1", 1),
    new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

虽然这种方法不是最简洁的,但

  • 它不需要任何java.util之外的东西
  • 它是类型安全的,并且容易适应不同类型的键和值。

如果需要,可以使用包含映射供应商的toMap签名来指定地图类型。 - zrvan

4
如果您只需要向映射中添加一个值,您可以使用 Collections.singletonMap 方法:
Map<K, V> map = Collections.singletonMap(key, value)

4

你的第二种方法(双括号初始化)被认为是一种反模式,因此我会选择第一种方法。

另一种初始化静态Map的简单方法是使用此实用函数:

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>(keyValues.length / 2);

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");

注意:在Java 9中,您可以使用Map.of

3

由于Java不支持map字面量,因此必须始终显式实例化和填充map实例。

幸运的是,可以使用工厂方法近似地模拟Java中的map字面量行为。

例如:

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

输出:

{a=1, b=2, c=3}

这比逐个创建和填充映射元素要方便得多。


3

我还没有看到过在任何答案中发布我使用(并且喜欢的)方法,所以在这里介绍一下:

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

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

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'来避免需要为方法的名称添加前缀)

我发现为其他集合(列表、集合、排序集、排序映射等)提供类似的静态方法很有用。

虽然不完全像json对象初始化那样好,但就可读性而言是向那个方向迈出的一步。


3

我不喜欢静态初始化器语法,也不相信匿名子类。一般来说,我同意使用静态初始化器和使用匿名子类的所有缺点,这些缺点在之前的回答中都有提到。另一方面,这些帖子中提出的优点对我来说还不够。我更喜欢使用静态初始化方法:

public class MyClass {
    private static final Map<Integer, String> myMap = prepareMap();

    private static Map<Integer, String> prepareMap() {
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "one");
        hashMap.put(2, "two");

        return hashMap;
    }
}

2
我已经阅读了答案,并决定编写自己的地图生成器。请随意复制粘贴并享受。
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * A tool for easy creation of a map. Code example:<br/>
 * {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
 * @param <K> key type (inferred by constructor)
 * @param <V> value type (inferred by constructor)
 * @author Vlasec (for https://dev59.com/1nRB5IYBdhLWcg3wyqOo#30345279)
 */
public class MapBuilder <K, V> {
    private Map<K, V> map = new HashMap<>();

    /** Constructor that also enters the first entry. */
    private MapBuilder(K key, V value) {
        and(key, value);
    }

    /** Factory method that creates the builder and enters the first entry. */
    public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
        return new MapBuilder<>(key, value);
    }

    /** Puts the key-value pair to the map and returns itself for method chaining */
    public MapBuilder<K, V> and(K key, V value) {
        map.put(key, value);
        return this;
    }

    /**
     * If no reference to builder is kept and both the key and value types are immutable,
     * the resulting map is immutable.
     * @return contents of MapBuilder as an unmodifiable map.
     */
    public Map<K, V> build() {
        return Collections.unmodifiableMap(map);
    }
}

编辑:最近,我经常发现公共静态方法of,并且我有点喜欢它。我将其添加到代码中,并使构造函数私有化,从而转换为静态工厂方法模式。

编辑2:最近,我不再喜欢名为of的静态方法,因为使用静态导入时看起来非常糟糕。我将其重命名为mapOf,使其更适合静态导入。


2

好的...我喜欢枚举类型 ;)

enum MyEnum {
    ONE   (1, "one"),
    TWO   (2, "two"),
    THREE (3, "three");

    int value;
    String name;

    MyEnum(int value, String name) {
        this.value = value;
        this.name = name;
    }

    static final Map<Integer, String> MAP = Stream.of( values() )
            .collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}

2
"

JEP 269 提供了一些方便的集合 API 工厂方法。这些工厂方法不在当前的 Java 版本(8)中,但计划在 Java 9 发布时加入。

对于 Map,有两个工厂方法:ofofEntries。使用 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来尝试这些特性。

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