ChatGPT解决这个技术问题 Extra ChatGPT

如何初始化静态地图?

您将如何在 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 中初始化地图:stackoverflow.com/a/37384773/1216775
请不要使用 double brace initialization - 这是一种黑客行为,也是一种泄漏内存和导致其他问题的简单方法。
爪哇9?如果条目计数 <= 10 使用 Map.of 否则 Map.ofEntries,请检查 stackoverflow.com/a/37384773/1216775

6
6 revs, 5 users 85%

在这种情况下,实例初始化程序只是语法糖,对吧?我不明白为什么你需要一个额外的匿名类来初始化。如果正在创建的类是最终的,它将不起作用。

您也可以使用静态初始化器创建不可变映射:

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我严重怀疑Android有这样的限制。这根本没有意义。快速搜索发现 this question here(和许多其他)似乎暗示您可以在 Android 中为 Map 对象使用 String 键。
所以没有人费心去调查,我可以确认在 Android 上为 Map 对象使用 String 键没有问题。
Jordan:现在这是一个老话题,但我怀疑@Luke 试图在具有不同键类型的映射中使用字符串作为键,例如 Map
J
Jonik

我喜欢初始化静态、不可变映射的 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 最流行和最有用的免费 3 方库之一,如 fellow SO users agree。 (如果您是新手,该链接后面有一些优秀的学习资源。)

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

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

更新(2016 年):作为 Tagir Valeev points outJava 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 仍未关闭,因此它可能会引入与集合文字相关的新功能
B
Bouramas

我会使用:

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 选项(无库)中,我最喜欢这个,因为映射定义显然与其初始化相关联。还同意常量命名。
我从来没有想过你可以做到这一点。
C
Chris Noe

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

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

该技术称为双括号初始化: stackoverflow.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.equalsAbstractMap 中定义,适用于 Map 的 any 子类,因此这里不必担心。钻石操作员的事情很烦人,但如前所述,现在已经解决了。
M
Michael Lihs

第二种方法的一个优点是您可以用 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!

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

Java 9+ 中的 Map.of

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 中执行此操作的唯一类型安全方法。
L
Luke Hutchison

这是一个 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<Integer, String>,您需要这样的东西:

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

编辑(2):i_am_zero 有一个更好的、支持混合类型的版本,它使用 new SimpleEntry<>(k, v) 调用流。查看该答案:https://stackoverflow.com/a/37384773/3950982


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

爪哇 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 个。

爪哇 8

我们可以创建一个映射条目流。我们已经在 java.util.AbstractMap 中实现了两个 Entry,它们是 SimpleEntrySimpleImmutableEntry。对于此示例,我们可以将前者用作:

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<>() 方式的可读性远不如静态 put() :/
D
Donald Raab

使用 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 更喜欢它。
e
eljenso

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

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 的来源,而那是我不想浪费的时间。任何对可读性的改进都是一种奖励,而且对性能的影响很小,所以对我来说这似乎是最好的选择。
P
Paŭlo Ebermann

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

更新:此库现在命名为 Guava


p
peterh

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

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

L
Leninkumar Koppoju
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 用于常量,否则它不能被视为常量。


D
Dennis C

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

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

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

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

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

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


a
agad

像往常一样 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 中使用 Map<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... ) 之类的行SuppressWarnings( unchecked )
@mikerodent 不是因为 Object[][] 吗?查看更新的 unswear - 我在 Eclipse 中没有任何警告。
多么奇怪......即使我去String[][]我也会收到“警告”!当然,这仅在您的 KV 是同一类时才有效。我认为您没有(可以理解)在 Eclipse 设置中将“未经检查的转换”设置为“忽略”?
n
neu242

这是我最喜欢的,如果我

不想(或不能)使用 Guava 的 ImmutableMap.of()

或者我需要一个可变的地图

或者我需要超过 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));

i
icza

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

注意:该问题并未说明有关使地图不可修改的任何内容,因此我将省略这一点,但知道使用 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
    }
}

B
Bharanidharan K

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

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");


B
Brian Agnew

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

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

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


在这种特殊情况下,它是静态的,因此没有外部实例。
可以说,XStream 不应该尝试序列化这样的东西(它是静态的。为什么需要序列化静态变量?)
P
Philip Guin

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

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

z
zrvan

在 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 签名来指定地图的类型。
S
Stromata

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

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

y
yegor256

您可以使用 Cactoos 中的 StickyMapMapEntry

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

R
R. Oosterholt

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

初始化静态 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


S
Stanley Borowy

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

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

j
josh

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

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

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

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)

(您可以使用“导入静态”来避免需要为方法名称添加前缀)

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

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


c
catch23

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

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


A
Ali Dehghani

JEP 269 为 Collections 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")
);

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


j
jglatre

嗯...我喜欢枚举;)

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

V
Vlasec

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

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://stackoverflow.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,使其更适合静态导入。