如何初始化静态地图?

dog*_*ane 1084 java collections dictionary initialization idiomatic

你会如何Map在Java中初始化静态?

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

各自的优点和缺点是什么?

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

import java.util.HashMap;
import java.util.Map;

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<Integer, String>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<Integer, String>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

Mis*_*ble 1074

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Luke我严重怀疑Android有这样的局限性.这根本没有意义.一个快速搜索发现[这个问题](http://stackoverflow.com/questions/3013538/use-flurryagent-oneventstring-eventid-mapstring-string-parameters)这里(和许多其他)似乎暗示你可以使用一个字符串Android中Map对象的键. (28认同)
  • 所以没有其他人不愿意调查,我可以确认在Android上使用String键对于Map对象没有问题. (11认同)
  • 乔丹:现在这是一个古老的话题,但我怀疑@Luke试图在一个具有不同键类型的地图中使用字符串作为键,例如Map <Integer,String>. (11认同)
  • 这是我多年来使用的成语,我从来没有人盯过它.我对不可修改的常量集和列表也这样做. (10认同)
  • 如何使用String键处理HashMap <String,String>.Map对象不允许我有一个String键,所以我不能使用unmodifiableMap().我想投射到HashMap也会破坏目的.有任何想法吗? (3认同)

Jon*_*nik 428

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

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

如您所见,它非常简洁(因为方便的工厂方法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();
Run Code Online (Sandbox Code Playgroud)

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

(其中一部分)Guava曾被称为Google Collections.如果您尚未在Java项目中使用此库,我强烈建议您尝试使用它!正如SO用户所认同的那样,Guava已经迅速成为Java最受欢迎和最有用的免费第三方库之一.(如果您是新手,那么该链接背后有一些优秀的学习资源.)


更新(2015):至于Java 8,我仍然会使用Guava方法,因为它比其他任何方式更清洁.如果您不想要Guava依赖,请考虑一个普通的旧init方法.如果你问我,那么带有二维数组和Stream API的hack 非常难看,如果你需要创建一个键和值不是同一类型的Map(比如Map<Integer, String>问题),那么它会变得更加丑陋.

至于番石榴的未来,就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"
);
Run Code Online (Sandbox Code Playgroud)

  • 好像我们的SO管理员已经删除了我链接的令人尊敬的"最有用的免费第三方Java库"问题.:(该死的. (20认同)
  • 我现在可以看到删除的问题(10k + rep),所以这里是**[最有用的免费第三方Java库的副本](https://dl.dropbox.com/u/2896691/Most-useful- Java的libs.html)**.这只是第一页,但至少你可以找到上面提到的[Guava资源](https://dl.dropbox.com/u/2896691/Most-useful-Java-libs.html#132639). (11认同)
  • 我同意,这是初始化常量贴图的最好方法.不仅更具可读性,而且自[Collections.unmodifiableMap](http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#unmodifiableMap(java.util.Map))返回底层映射的只读视图(仍可以修改). (2认同)
  • 我真的更喜欢这种方法,虽然知道如何在没有额外依赖性的情况下做到这一点是有益的. (2认同)
  • [JEP 186](http://openjdk.java.net/jeps/186)仍未关闭,因此它可能会引入与集合文字相关的新功能 (2认同)

Chr*_*Noe 176

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

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

  • 这种技术称为双括号初始化:http://stackoverflow.com/questions/1372113/meaning-of-new-class-initialization-idiom/1372124#1372124这不是一种特殊的Java 5语法,它只是一个匿名的技巧具有实例初始化程序的类. (43认同)
  • 关于双括号初始化的快速问题:执行此操作时,Eclipse会发出有关缺少序列ID的警告.一方面,我不明白为什么在这种特定情况下需要一个序列号ID,但另一方面,我通常不喜欢抑制警告.你对此有何看法? (13认同)
  • @nbarraille那是因为`HashMap实现了Serializable`.由于您实际使用此"技巧"创建了HashMap的子类,因此您隐式创建了一个Serializable类.为此你应该提供一个serialUID. (8认同)
  • `从非静态上下文使用时,双括号初始化会导致内存泄漏,因为创建的匿名类将保持对周围对象的引用.由于需要额外的类加载,它的性能比常规初始化差.如果equals()方法不接受子类作为参数,则可能导致equals()比较失败.最后,在Java 9之前,它不能与菱形运算符结合使用,因为它不能与匿名类一起使用. - IntelliJ (4认同)
  • @MarkJeronimus - 建议使用*是*静态上下文.性能可能更差,但在处理可能少量静态定义的地图时并不明显.`HashMap.equals`在`AbstractMap`中定义,并且在Map的*any*子类上工作,所以这里不需要考虑.钻石操作员的事情令人讨厌,但正如所提到的那样现在已经解决了. (2认同)

Pet*_*aný 173

我会用:

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

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<Integer, String>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 它避免了匿名课程,我个人认为这是一种不好的风格,并避免
  2. 它使地图的创建更加明确
  3. 它使地图不可修改
  4. 因为MY_MAP是不变的,我会把它命名为常量

  • 在纯JDK选项(没有库)中,我最喜欢这个,因为地图定义明显与其初始化相关联.也同意不断命名. (2认同)

Out*_*mer 94

第二种方法的一个优点是你可以用它来包装,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!
Run Code Online (Sandbox Code Playgroud)

  • 您是否可以通过将new运算符移动到static {}块并包装它来在第一种方法中轻松完成此操作? (3认同)
  • 无论如何,我会将构造函数调用移动到静态初始化中.其他任何看起来都很奇怪. (2认同)
  • 任何想法使用匿名类而不是具体类可能会有什么表现? (2认同)
  • 这不是真正推荐的:https://rules.sonarsource.com/java/RSPEC-3599 (2认同)

Luk*_*son 61

这是一个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]));
Run Code Online (Sandbox Code Playgroud)

编辑:要Map<Integer, String>在问题中初始化a ,你需要这样的东西:

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]));
Run Code Online (Sandbox Code Playgroud)

编辑(2):i_am_zero有一个更好的,具有混合类型功能的版本,它使用一组new SimpleEntry<>(k, v)调用.看看答案:https://stackoverflow.com/a/37384773/3950982

  • 我冒昧地添加了一个与问题和其他答案等效的版本:init一个Map的键和值是不同类型的(所以`String [] []`不会这样做,`Object [] []`需要).恕我直言,这种做法很丑陋(演员阵容更是如此),难以记住; 不会自己用它. (6认同)
  • 要在Java 8中初始化地图:http://stackoverflow.com/a/37384773/1216775 (2认同)

Tag*_*eev 50

在Java 9中:

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

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

  • 这很干净,直到你意识到[如何实现](http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/86f19074aad2/src/java.base/share/classes/java/ UTIL/Map.java#l1564) (6认同)
  • 或者,如果您想要超过10个键值对,可以使用`Map.ofEntries` (5认同)
  • JDK 中的实现清洁度应该无关紧要,只要它可以工作并满足契约即可。像任何黑匣子一样,如果真的需要,实现细节总是可以在未来修复...... (4认同)

i_a*_*ero 37

Java 9

我们可以Map.ofEntries用作:

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"));
Run Code Online (Sandbox Code Playgroud)

我们也可以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));
Run Code Online (Sandbox Code Playgroud)

  • `new SimpleEntry&lt;&gt;()` 方式远不如静态 `put()` 可读:/ (3认同)

Don*_*aab 31

使用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");
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用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");
} 
Run Code Online (Sandbox Code Playgroud)

注意:我是Eclipse Collections的提交者


elj*_*nso 28

在这种情况下,我永远不会创建一个匿名子类.如果您想使地图不可修改,静态初始化程序也可以正常工作:

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 永远不要初始化集合. (6认同)
  • @rookie在其他有利于静态init的答案中给出了几个原因.这里的目标_is_初始化,所以为什么要引入子类,除了可能保存一些键击?(如果你想节省击键次数,那么Java绝对不是一种编程语言的好选择.)我在Java编程时使用的一条经验法则是:尽可能少地使用子类(从不合理地避免使用它). (3认同)

Kaa*_*rel 18

也许有趣的是查看Google Collections,例如他们在页面上显示的视频.它们提供了各种方法来初始化地图和集合,并提供不可变的集合.

更新:此库现在名为Guava.


小智 17

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

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


Len*_*oju 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们声明了多个常量,那么该代码将被编写为静态块,并且将来很难维护.所以最好使用匿名类.

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

并且建议使用不可修改的Map作为常量,否则它不能被视为常量.


Den*_*s C 10

我强烈建议静态块样式的"双支撑初始化"样式.

有人可能会评论他们不喜欢匿名类,开销,性能等.

但我更考虑的是代码的可读性和可维护性.在这一点上,我站在一个双支撑是一个更好的代码风格而不是静态方法.

  1. 元素是嵌套和内联的.
  2. 这是更多的OO,而不是程序性的.
  3. 性能影响非常小,可以忽略不计.
  4. 更好的IDE大纲支持(而不是许多匿名静态{}阻止)
  5. 你保存了几行评论来为他们建立关系.
  6. 从异常和字节码优化器中防止未初始化对象的可能元素泄漏/实例引导.
  7. 不用担心静态块的执行顺序.

另外,你知道匿名类的GC,你总是可以使用它将它转换为普通的HashMap new HashMap(Map map).

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


aga*_*gad 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"}
 });
Run Code Online (Sandbox Code Playgroud)


neu*_*242 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]);
        }
    }};
}
Run Code Online (Sandbox Code Playgroud)

它非常紧凑,它忽略了杂散值(即没有值的最终键).

用法:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));
Run Code Online (Sandbox Code Playgroud)


Bha*_*n K 7

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

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


icz*_*cza 6

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

注意:这个问题并没有说明使地图不可修改的问题,所以我会把它留下来,但要知道它可以很容易地完成Collections.unmodifiableMap(map).

第一个提示

第一个提示是您可以对地图进行本地引用,并为其指定SHORT名称:

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");
}
Run Code Online (Sandbox Code Playgroud)

第二个提示

第二个提示是您可以创建一个辅助方法来添加条目; 如果你想要,你也可以公开这个帮助方法:

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);
}
Run Code Online (Sandbox Code Playgroud)

这里的辅助方法不可重用,因为它只能添加元素myMap2.为了使它可以重用,我们可以使map本身成为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
    }
}
Run Code Online (Sandbox Code Playgroud)


Bri*_*new 5

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

话虽如此,只要你知道,这种方法就可以了.我大部分时间用它来以简洁的方式初始化各种系列.

编辑:在评论中正确指出这是一个静态类.显然我没有仔细阅读这篇文章.不过我的意见仍然适用于匿名内部类.

  • 在这种特殊情况下,它是静态的,因此没有外部实例. (3认同)

Phi*_*lip 5

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

static final Map<String, Integer> map = MapUtils.unmodifiableMap(
    String.class, Integer.class,
    "cat",  4,
    "dog",  2,
    "frog", 17
);
Run Code Online (Sandbox Code Playgroud)

此实现应捕获任何错误:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

817772 次

最近记录:

6 年 前