如何初始化静态地图?

Posted

技术标签:

【中文标题】如何初始化静态地图?【英文标题】:How can I initialise a static Map? 【发布时间】:2010-10-05 04:32:58 【问题描述】:

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

【问题讨论】:

用于在 Java 8 中初始化地图:***.com/a/37384773/1216775 请不要使用double brace initialization - 这是一种黑客行为,也是一种泄漏内存和导致其他问题的简单方法。 Java 9?如果条目数 Map.of 否则使用 Map.ofEntries,请检查 ***.com/a/37384773/1216775 【参考方案1】:

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

【讨论】:

这是我多年来一直使用的成语,我从来没有人看过它。我也对不可修改的常量 Sets 和 Lists 做同样的事情。 我将如何处理带有 String 键的 HashMap。 Map 对象不允许我拥有 String 键,因此我不能使用 unmodifiableMap()。我想强制转换为 HashMap 也会破坏目的。有什么想法吗? @Luke 我严重怀疑安卓有这样的限制。这根本没有意义。快速搜索发现this question here(和许多其他)似乎暗示您可以在 android 中使用 String 键作为 Map 对象。 所以没有人费心去调查,我可以确认在 Android 上为 Map 对象使用 String 键没有问题。 乔丹:现在这是一个老话题,但我怀疑@Luke 试图在具有不同键类型的映射中使用字符串作为键,例如地图.【参考方案2】:

我喜欢Guava 初始化静态、不可变映射的方式:

static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);

如您所见,它非常简洁(因为ImmutableMap 中方便的工厂方法)。

如果您希望地图的条目超过 5 个,则不能再使用ImmutableMap.of()。相反,请按照以下思路尝试ImmutableMap.builder()

static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();

要详细了解 Guava 的不可变收集实用程序的好处,请参阅 Immutable Collections Explained in Guava User Guide。

(一个子集)Guava 曾经被称为 Google Collections。如果您还没有在您的 Java 项目中使用这个库,我强烈建议您尝试一下! Guava 已迅速成为 Java 中最受欢迎和最有用的免费 3rd 方库之一,如fellow SO users agree。 (如果您是新手,该链接后面有一些优秀的学习资源。)


更新(2015 年):至于 Java 8,我仍然会使用 Guava 方法,因为它比其他任何方法都干净。如果您不想依赖 Guava,请考虑使用 plain old init method。如果您问我,two-dimensional array and Stream API 的 hack 非常丑陋,如果您需要创建一个键和值不是同一类型的 Map(例如问题中的 Map&lt;Integer, String&gt;),则会变得更丑陋。

就总体而言 Guava 的未来,关于 Java 8,Louis Wasserman said this 早在 2014 年,而 [update] 在 2016 年宣布 Guava 21 will require and properly support Java 8


更新(2016 年):作为Tagir Valeev points out,Java 9 最终将通过为集合添加 convenience factory methods 来使用纯 JDK 来完成这项工作:

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);

【讨论】:

似乎我们的 SO 管理员同事删除了我链接到的古老的“最有用的免费第三方 Java 库”问题。 :( 该死的。 我同意,这是初始化常量映射的最佳方式。不仅更具可读性,而且因为 Collections.unmodifiableMap 返回底层地图的只读视图(仍然可以修改)。 我现在可以看到已删除的问题(有 10k+ 代表),所以这里是 copy of 'Most useful free third-party Java libraries'。这只是第一页,但至少你可以找到上面提到的Guava resources。 我真的很喜欢这种方法,尽管知道如何在没有额外依赖的情况下这样做是有益的。 JEP 186 仍未关闭,因此可能会引入与集合字面量相关的新功能【参考方案3】:

我会使用:

public class Test 
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() 
        Map<Integer, String> result = new HashMap<>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    

    它避免了匿名类,我个人认为这是一种不好的风格,并且避免 它使地图的创建更加明确 它使地图不可修改 由于 MY_MAP 是常量,我将其命名为常量

【讨论】:

在纯 JDK 选项(无库)中,我最喜欢这个,因为映射定义显然与其初始化相关联。还同意常量命名。 我从来没有想过你可以做到这一点。【参考方案4】:

Java 5 提供了这种更紧凑的语法:

static final Map<String , String> FLAVORS = new HashMap<String , String>() 
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
;

【讨论】:

这种技术被称为双括号初始化:***.com/questions/1372113/… 这不是特殊的 Java 5 语法,它只是一个带有实例初始化器的匿名类的技巧。 关于双括号初始化的快速问题:执行此操作时,Eclipse 会发出关于缺少序列 ID 的警告。一方面,我不明白为什么在这种特定情况下需要序列号,但另一方面,我通常不喜欢压制警告。您对此有何看法? @nbarraille 那是因为HashMap implements Serializable。由于您实际上使用此“技巧”创建了 HashMap 的子类,因此您隐式地创建了一个 Serializable 类。为此,您应该提供一个 serialUID。 Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes. – IntelliJ @MarkJeronimus - 建议使用 is 静态上下文。性能可能会更差,但在处理可能少量的静态定义的地图时并不明显。 HashMap.equals 定义在 AbstractMap 中,适用于 Map 的 any 子类,因此这里不用担心。钻石操作员的事情很烦人,但如前所述,现在已经解决了。【参考方案5】:

第二种方法的一个优点是您可以用Collections.unmodifiableMap() 包装它,以保证以后不会更新集合:

private static final Map<Integer, String> CONSTANT_MAP = 
    Collections.unmodifiableMap(new HashMap<Integer, String>()  
        put(1, "one");
        put(2, "two");
    );

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!

【讨论】:

你不能在第一种方法中通过将新运算符移动到静态 块中并包装它来轻松地做到这一点吗? 无论如何我都会将构造函数调用移动到静态初始化中。其他任何东西看起来都很奇怪。 知道使用匿名类而不是具体类可能会对性能造成什么影响吗?【参考方案6】:

Map.of 在 Java 9+ 中

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

详情请见JEP 269。 JDK 9 在 2017 年 9 月达到了general availability。

【讨论】:

或者如果你想要超过10个键值对,你可以使用Map.ofEntries 这很干净,直到你意识到how it was implemented 哎呀好难过 - 看起来它只支持 10 个条目,之后您需要使用 ofEntries。跛脚。 JDK 中的实现清洁度应该无关紧要,只要它可以工作并满足合同。像任何黑匣子一样,如果真的需要,实现细节总是可以在将来修复...... @mid 这是 Java 中唯一的类型安全方式。【参考方案7】:

这是一个 Java 8 单行静态映射初始化器:

private static final Map<String, String> EXTENSION_TO_MIMETYPE =
    Arrays.stream(new String[][] 
         "txt", "text/plain" , 
         "html", "text/html" , 
         "js", "application/javascript" ,
         "css", "text/css" ,
         "xml", "application/xml" ,
         "png", "image/png" , 
         "gif", "image/gif" , 
         "jpg", "image/jpeg" ,
         "jpeg", "image/jpeg" , 
         "svg", "image/svg+xml" ,
    ).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));

编辑:要在问题中初始化 Map&lt;Integer, String&gt;,您需要这样的东西:

static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]
        1, "one",
        2, "two",
).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));

Edit(2):i_am_zero 有一个更好的混合类型版本,它使用new SimpleEntry&lt;&gt;(k, v) 调用流。查看答案:https://***.com/a/37384773/3950982

【讨论】:

我冒昧地添加了一个与问题和其他答案等效的版本:初始化一个 Map ,其键和值是不同类型的(所以String[][] 不会这样做,Object[][]需要)。恕我直言,这种方法很丑陋(演员阵容更是如此)并且很难记住;自己不会用。【参考方案8】:

Java 9

我们可以使用Map.ofEntries,调用Map.entry( 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"));

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

Java 8

我们可以创建一个地图条目流。我们已经在java.util.AbstractMap 中有两个Entry 的实现,它们是SimpleEntry 和SimpleImmutableEntry。对于这个例子,我们可以将前者用作:

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

【讨论】:

new SimpleEntry&lt;&gt;() 方式的可读性远不如静态put() :/【参考方案9】:

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

【讨论】:

我真希望 Eclipse Collections 是 Java 的默认集合库。我比 Guava + JCL 更喜欢它。【参考方案10】:

在这种情况下,我永远不会创建匿名子类。静态初始化器同样可以很好地工作,例如,如果您想让地图不可修改:

private static final Map<Integer, String> MY_MAP;
static

    Map<Integer, String>tempMap = new HashMap<Integer, String>();
    tempMap.put(1, "one");
    tempMap.put(2, "two");
    MY_MAP = Collections.unmodifiableMap(tempMap);

【讨论】:

那在什么情况下你会使用匿名子类来初始化hashmap呢? 从不初始化集合。 您能解释一下为什么使用静态初始化器比创建匿名子类更好吗? @rookie 在其他支持静态初始化的答案中给出了几个原因。这里的目标进行初始化,那么为什么要引入子类,除了可能节省一些击键? (如果你想节省击键次数,Java 绝对不是一种编程语言的好选择。)我在 Java 中编程时使用的一条经验法则是:尽可能少地子类化(并且永远不要在可以合理避免的情况下)。 @eljenso - 我通常赞成子类语法的原因是它将初始化内联,它所属的地方。次优选择是调用返回初始化映射的静态方法。但我担心我会查看您的代码,并且不得不花几秒钟时间找出 MY_MAP 的来源,而那是我不想浪费的时间。任何对可读性的改进都是一种奖励,而且对性能的影响很小,所以对我来说这似乎是最好的选择。【参考方案11】:

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

更新:这个库现在被命名为Guava。

【讨论】:

【参考方案12】:

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

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

【讨论】:

【参考方案13】:
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 用于常量,否则不能视为常量。

【讨论】:

【参考方案14】:

我强烈建议使用“双括号初始化”样式而不是静态块样式。

有人可能会评论说他们不喜欢匿名类、开销、性能等。

但我更多考虑的是代码的可读性和可维护性。从这个角度来看,我认为双括号是一种更好的代码风格而不是静态方法。

    元素是嵌套和内联的。 更多的是面向对象,而不是程序。 性能影响非常小,可以忽略不计。 更好的 IDE 大纲支持(而不是许多匿名静态 块) 您保存了几行评论以建立关系。 防止异常和字节码优化器可能导致未初始化对象的元素泄漏/实例导致。 不用担心静态块的执行顺序。

另外,如果你知道匿名类的GC,你可以随时使用new HashMap(Map map)将其转换为普通的HashMap。

您可以这样做,直到遇到另一个问题。如果你这样做了,你应该使用完整的另一种编码风格(例如,没有静态,工厂类)。

【讨论】:

【参考方案15】:

像往常一样 apache-commons 有正确的方法 MapUtils.putAll(Map, Object[]):

例如,要创建一个颜色图:

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

【讨论】:

我在所有构建中都包含 Apache Commons,因此,不幸的是,在纯 Java 中没有方法 Arrays.asMap( ... ),我认为这是最好的解决方案。重新发明***通常是愚蠢的。非常轻微的缺点是,使用泛型它需要未经检查的转换。 @mikerodent 4.1 版本是通用的:public static Map putAll(final Map map, final Object[] array) Tx ... 是的,我使用的是 4.1,但我仍然需要在 Eclipse 中使用 SuppressWarnings( unchecked ) 使用 Map&lt;String, String&gt; dummy = MapUtils.putAll(new HashMap&lt;String, String&gt;(), new Object[][]... ) 之类的行 @mikerodent 不是因为Object[][]吗?查看更新的 unswear - 我在 Eclipse 中没有任何警告。 多么奇怪......即使我去String[][] 我也得到了“警告”!当然,只有当你的 KV 是同一个班级时才有效。我认为您没有(可以理解)在 Eclipse 设置中将“未经检查的转换”设置为“忽略”?【参考方案16】:

这是我的最爱

不想(或不能)使用 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));

【讨论】:

【参考方案17】:

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

注意:这个问题并没有说明要使地图不可修改,所以我将把它省略,但知道使用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
    

【讨论】:

【参考方案18】:

如果你想要不可修改的地图,最后java 9在Map接口中添加了一个很酷的工厂方法of。 Set、List 中也添加了类似的方法。

Map&lt;String, String&gt; unmodifiableMap = Map.of("key1", "value1", "key2", "value2");

【讨论】:

【参考方案19】:

您正在创建的匿名类运行良好。但是您应该知道这是一个 inner 类,因此,它将包含对周围类实例的引用。所以你会发现你不能用它做某些事情(使用XStream 之一)。你会得到一些非常奇怪的错误。

话虽如此,只要您知道,那么这种方法就可以了。我大部分时间都用它来以简洁的方式初始化各种集合。

编辑:在 cmets 中正确指出这是一个静态类。显然我没有仔细阅读这个。但是我的 cmets 确实仍然适用于匿名内部类。

【讨论】:

在这种特殊情况下它是静态的,所以没有外部实例。 可以说 XStream 不应该尝试序列化这样的东西(它是静态的。为什么需要序列化静态变量?)【参考方案20】:

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

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

【讨论】:

【参考方案21】:

在 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签名来指定地图类型。【参考方案22】:

如果只需要在地图上添加一个值,可以使用Collections.singletonMap:

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

【讨论】:

【参考方案23】:

您可以使用来自Cactoos 的StickyMapMapEntry

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

【讨论】:

【参考方案24】:

你的第二种方法(双括号初始化)被认为是anti pattern,所以我会选择第一种方法。

另一种初始化静态地图的简单方法是使用这个实用函数:

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

【讨论】:

【参考方案25】:

我不喜欢静态初始化器语法,也不相信匿名子类。一般来说,我同意使用静态初始化程序的所有缺点以及使用之前答案中提到的匿名子类的所有缺点。另一方面 - 这些帖子中介绍的专业人士对我来说还不够。我更喜欢使用静态初始化方法:

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;
    

【讨论】:

【参考方案26】:

我没有在任何答案中看到我使用(并且已经越来越喜欢)的方法,所以这里是:

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

相反,我更喜欢如下所示的初始化:

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'来避免需要在方法名前加上前缀)

我发现为其他集合(list、set、sortedSet、sortedMap 等)提供类似的静态方法很有用

它不如 json 对象初始化那么好,但就可读性而言,这是朝着这个方向迈出的一步。

【讨论】:

【参考方案27】:

因为 Java 不支持地图文字,地图实例必须始终显式实例化和填充。

幸运的是,可以使用 工厂方法 来近似 Java 中映射文字的行为。

例如:

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

这比一次创建和填充地图要方便得多。

【讨论】:

【参考方案28】:

JEP 269 为 Collections API 提供了一些便利的工厂方法。此工厂方法不在当前的 Java 版本中,即 8,但计划在 Java 9 版本中发布。

对于Map,有两种工厂方法:ofofEntries。使用of,您可以传递交替的键/值对。例如,为了创建一个Map,比如age: 27, major: cs

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

ofofEntries 都将返回一个不可变的Map,因此您无法在构造后更改它们的元素。您可以使用JDK 9 Early Access 试用这些功能。

【讨论】:

【参考方案29】:

嗯...我喜欢枚举;)

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

【讨论】:

【参考方案30】:

我已阅读答案并决定编写自己的地图生成器。随意复制粘贴并享受。

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 http://***.com/a/30345279/1977151)
 */
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,我有点喜欢它。我将它添加到代码中并将构造函数设为私有,从而切换到静态工厂方法模式。

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

【讨论】:

以上是关于如何初始化静态地图?的主要内容,如果未能解决你的问题,请参考以下文章

如何进行地图SDK开发

如何进行地图SDK开发

如何进行地图SDK开发

如何初始化一个iOS原生地图

如何初始化一个以结构为值的地图?

如何在 OpenLayers 中设置初始地图区域?