如何初始化静态地图?


1131

您将如何Map在Java中初始化静态变量?

方法一:静态初始化器
方法二:实例初始化器(匿名子类)或其他方法?

各自的优缺点是什么?

这是说明两种方法的示例:

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
请不要使用双花括号初始化 -这是一种技巧,也是泄漏内存并引起其他问题的简便方法。
dimo414

Java 9?如果条目计数<= 10 ,请使用Map.ofelse Map.ofEntries,请检查stackoverflow.com/a/37384773/1216775
akhil_mittal,

Answers:


1106

在这种情况下,实例初始化器只是语法糖,对吗?我不明白为什么您只需要一个额外的匿名类即可进行初始化。如果正在创建的类是最终的,那将是行不通的。

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

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

10
这是我使用多年的成语,而且从来没有人看过它。对于不可修改的常量Set和List,我也是如此。
jasonmp85

3
我将如何使用String键处理HashMap <String,String>。Map对象不允许我具有String键,因此不能使用unmodifiableMap()。我想强制转换为HashMap也会达到目的。有任何想法吗?
路加福音

30
@Luke我严重怀疑Android是否有这样的限制。这根本没有意义。快速搜索在这里(以及许多其他问题)中发现了这个问题,这似乎意味着您可以在Android中为Map对象使用String键。
mluisbrown

11
因此,没有其他人愿意去调查,我可以确认在Android上为Map对象使用String键没有问题。
约旦

11
Jordan:现在这是一个古老的话题,但是我怀疑@Luke试图在具有不同键类型的映射中使用字符串作为键,例如Map <Integer,String>。
悲惨变量

445

我喜欢使用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();

要了解更多有关的番石榴的不可变集合实用工具的好处,看到解释不可改变的集合番石榴用户指南中

番石榴(的一部分)曾经被称为Google Collections。如果您尚未在Java项目中使用此库,强烈建议您尝试一下!正如其他SO用户所同意的那样,Guava已迅速成为Java最受欢迎和最有用的免费第三方库之一。(如果您不熟悉它,则该链接后面有一些出色的学习资源。)


更新(2015):关于Java 8,我仍然会使用Guava方法,因为它比其他任何方法都更干净。如果您不希望Guava依赖,请考虑使用普通的init方法。如果您问我,使用二维数组和Stream API的骇客会很难看,如果您需要创建一个键和值不是同一类型的Map(例如Map<Integer, String>在问题中一样)。

至于Guava的未来,就Java 8而言,Louis Wasserman曾在2014年和2016 年说过 [ 更新 ],它宣布Guava 21将需要并适当地支持Java 8


更新(2016年):正如Tagir Valeev所指出的那样Java 9最终将通过为集合添加便捷的工厂方法来使此干净,仅使用纯JDK即可完成:

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

21
好像我们的SO管理员同事删除了我链接的古老的“最有用的免费第三方Java库”问题。:(该死他们。
Jonik

2
我同意,这是初始化常量映射的最好方法。不仅更具可读性,而且因为Collections.unmodifiableMap返回基础地图的只读视图(仍然可以修改)。
crunchdog

11
现在,我可以看到已删除的问题(超过 10k的代表),因此这里是“最有用的免费第三方Java库”副本。它只是第一页,但至少您可以找到上面提到的Guava资源
Jonik

2
我确实更喜欢这种方法,尽管知道如何在没有额外依赖的情况下这样做是有益的。
扳手

2
JEP 186仍未关闭,因此它可能会引入与集合文字相关的新功能
cybersoft

182

我会用:

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);
    }
}
  1. 它避免了匿名类,我个人认为这是一种不好的风格,并且避免
  2. 它使地图的创建更加明确
  3. 它使地图无法修改
  4. 因为MY_MAP是常数,所以我将其命名为常数

3
在纯JDK选项(无库)中,我最喜欢这样做,因为地图定义显然与其初始化相关。也同意持续命名。
Jonik 2013年

我从未想到您可以做到这一点。
romulusnr

181

Java 5提供了以下更紧凑的语法:

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

46
该技术称为双括号初始化:stackoverflow.com/questions/1372113/…这不是Java 5的特殊语法,它只是使用带有实例初始化程序的匿名类的技巧。
Jesper 2010年

13
有关双括号初始化的快速问题:执行此操作时,Eclipse发出有关缺少序列ID的警告。一方面,我不明白为什么在这种特定情况下需要序列号,但另一方面,我通常不喜欢抑制警告。您对此有何想法?
nbarraille

8
@nbarraille那是因为HashMap implements Serializable。由于实际上使用此“技巧”创建了HashMap的子类,因此隐式创建了Serializable类。为此,您应该提供一个serialUID。
2013年

5
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
Mark Jeronimus

3
@MarkJeronimus-建议的用法静态上下文。性能可能会较差,但在处理数量较少的静态定义的映射时却不会如此明显。 HashMap.equals是在Map的任何子类中定义的AbstractMap并且可以在Map的任何子类上使用,因此在此无需担心。钻石操作员的事情很烦人,但是如上所述已经解决了。
Jules

95

第二种方法的一个优点是您可以将其包装起来,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!

3
您不能通过将新运算符移到静态{}块并将其包装来在第一种方法中轻松地做到这一点吗?
Patrick

2
无论如何,我都会将构造函数调用移到静态初始化中。其他一切看起来都很奇怪。
汤姆·霍顿-钉路

2
知道使用匿名类而不是具体类会对性能产生什么影响?
Kip

62

这是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>在问题中初始化一个as,您需要这样的东西:

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<>(k, v)调用流。查看该答案:https : //stackoverflow.com/a/37384773/3950982


7
我冒昧地增加一个版本就相当于与问题等回答:初始化一个Map,它的键和值是不同类型的(所以String[][]不会做,Object[][]需要)。恕我直言,这种方法很丑陋(演员更是如此),而且难以记住;我自己不会使用它。
约尼克

57

Map.of 在Java 9+中

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

有关详细信息,请参见JEP 269。JDK 9 于2017年9月全面上市


7
或者,如果您想要超过10个键值对,则可以使用Map.ofEntries
ZhekaKozlov,

8
一切都很干净,直到您意识到它是如何实现的
中旬

多数民众赞成在如此可悲-看起来它仅支持10个条目,之后您需要使用ofEntries。瘸。
Somaiah Kumbera '18

2
只要JDK可以工作并满足合同要求,它的实现清洁度就无关紧要。像任何黑匣子一样,将来可以在确实需要的情况下固定实现细节……
vikingsteve,

@mid这是在Java中执行此操作的唯一类型安全方式。
卢克·哈奇森

44

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

我们也可以使用 Map.of由Tagir在他的回答表明这里,但我们不能使用超过10个条目Map.of

Java 8(整洁的解决方案)

我们可以创建一个地图条目流。我们已经有两个实现Entryjava.util.AbstractMap它们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));

2
这种new SimpleEntry<>()方式比静态方式可读性差得多put():/
Danon

32

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


1
我真的希望Eclipse Collections是Java的默认集合库。我比Guava + JCL更喜欢它。
肯尼·卡森

29

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

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

1
在哪种情况下,您将使用匿名子类来初始化哈希图?
dogbane

6
从不初始化集合。
eljenso

您能解释为什么使用静态初始化程序比创建匿名子类更好的选择吗?
leba-lev 2012年

3
@rookie在其他答案中,有很多原因都支持静态init。这里的目标初始化,那么为什么要引入子类,除了可能节省一些击键之外?(如果您想节省击键次数,那么Java作为编程语言绝对不是一个好选择。)我在Java编程时使用的一条经验法则是:尽可能少地子类化(并且永远不要避免合理地避免子类化)。
eljenso 2012年

@eljenso-之所以我通常偏爱子类语法,是因为它将初始化放在其所属的行中。第二好的选择是调用返回初始化地图的静态方法。但是,恐怕我会看一下您的代码,而不得不花几秒钟的时间来弄清MY_MAP的来源,那是我不想浪费的时间。可读性的任何改进都是一个奖励,并且对性能的影响很小,因此对我来说似乎是最佳选择。
Jules

18

检出Google收藏夹也许很有趣,例如他们页面上的视频。它们提供了多种初始化映射和集合的方法,并且还提供了不可变的集合。

更新:该库现在名为Guava


17

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

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

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,则始终可以使用以下命令将其转换为普通的HashMap new HashMap(Map map)

您可以执行此操作,直到遇到另一个问题。如果这样做,则应为其使用完整的另一种编码样式(例如,无静态,工厂类)。


8

像往常一样,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,因此,不幸的是,Arrays.asMap( ... )在纯Java中缺少方法时,我认为这是最好的解决方案。重塑轮子通常很愚蠢。非常轻微的缺点是,使用泛型将需要未经检查的转换。
麦克啮齿动物,

@mikerodent 4.1版本是通用的:public static <K,V> Map <K,V> putAll(final Map <K,V> map,final Object [] array)
agad

Tx ...是的,我使用的是4.1,但是我仍然必须SuppressWarnings( unchecked )在Eclipse中使用这样的代码Map<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
mike rodent

@mikerodent不是因为Object [] []吗?请参阅更新的未使用-Eclipse中没有任何警告。
agad

多么奇怪……即使我走了,String[][]我也得到了“警告”!当然,这仅在您KV班级相同时才有效。我认为您没有(可以理解)没有在Eclipse设置中将“未经检查的转换”设置为“忽略”?
麦克啮齿动物,

7

当我不想(或不能)使用番石榴时ImmutableMap.of(),或者如果我需要可变的东西,这是我的最爱Map

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

如果您想要不可修改的地图,则Java 9最终ofMap接口中添加了一个很酷的工厂方法。类似的方法也被添加到“设置”,“列表”中。

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


6

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

注意:问题并没有说明使地图不可修改,因此我将省略它,但是知道可以使用轻松完成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);
}

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

第三提示

第三个技巧是您可以使用填充功能创建一个可重用的类似于构建器的帮助器类。这实际上是一个简单的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
在这种情况下,它是静态的,因此没有外部实例。
汤姆·哈特芬

可以说XStream不应尝试序列化这样的东西(它是静态的。为什么要序列化静态变量?)
jasonmp85 2010年

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

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 2015年


4

您可以使用StickyMapMapEntryCactoos

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

4

您的第二种方法(Double Brace初始化)被认为是一种反模式,因此我会选择第一种方法。

初始化静态映射的另一种简便方法是使用以下实用程序功能:

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

我不喜欢静态初始值设定项语法,也无法说服匿名子类。通常,我同意使用静态初始化程序的所有缺点,也可以使用先前答案中提到的使用匿名子类的所有缺点。另一方面,这些职位上的专业人士对我来说还不够。我更喜欢使用静态初始化方法:

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

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

我发现为其他集合(列表,集合,sortedSet,sortedMap等)提供类似的静态方法很有用。

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


3

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

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


2

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

因为Map有两种工厂方法:ofofEntries。使用of,您可以传递交替的键/值对。例如,为了创建一个Maplike {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尝试这些功能。


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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.