如何直接初始化HashMap(以字面方式)?

jen*_*ens 990 java collections dictionary initialization

有没有像这样初始化Java HashMap的方法?:

Map<String,String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};
Run Code Online (Sandbox Code Playgroud)

什么是正确的语法?我没有发现任何有关此事的内容.这可能吗?我正在寻找最短/最快的方法,将一些"最终/静态"值放在一个永不改变的地图中,并在创建Map时提前知道.

yan*_*kee 1202

对于Java 9或更高版本:

是的,现在可以了.在Java 9中添加了几个简化地图创建的工厂方法:

// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
    "a", "b",
    "c", "d"
);

// this works for any number of elements:
Map<String, String> test2 = Map.ofEntries(
    entry("a", "b"),
    entry("c", "d")
);
Run Code Online (Sandbox Code Playgroud)

在以上两个例子testtest2将是相同的,只是表达地图的不同方式.该Map.of方法在地图中定义了最多十个元素,而该Map.ofEntries方法没有这样的限制.

请注意,在这种情况下,生成的地图将是不可变的地图.如果您希望地图是可变的,您可以再次复制它,例如使用mutableMap = new HashMap<>(Map.of("a", "b"));

(另见JEP 269Javadoc)

对于Java版本8:

不,您必须手动添加所有元素.您可以在匿名子类中使用初始化程序来使语法更短一些:

Map<String, String> myMap = new HashMap<String, String>() {{
        put("a", "b");
        put("c", "d");
    }};
Run Code Online (Sandbox Code Playgroud)

但是,在某些情况下,匿名子类可能会引入不需要的行为.

使用函数进行初始化将执行相同的操作但可以提高代码的可读性:

Map<String, String> myMap = createMap();

private static Map<String, String> createMap() {
    Map<String,String> myMap = new HashMap<String,String>();
    myMap.put("a", "b");
    myMap.put("c", "d");
    return myMap;
}
Run Code Online (Sandbox Code Playgroud)

  • @Michael:是的,如果你想使用一个函数,你就不能使用not-function.但你为什么要这样? (9认同)
  • 对于需要带有单个条目的Map的情况,有`Collections.singletonMap()`:) (5认同)
  • 如果要初始化函数中的元素,这将不起作用... (3认同)
  • 既然已经发布了稳定的Java 9,我更喜欢[Javadoc的这个链接](https://docs.oracle.com/javase/9​​/docs/api/java/util/Map.html).并且+1因为少依赖! (2认同)
  • Java 9`entry`在哪里记录? (2认同)
  • 应该是“ Map.entry”,而不是“ entry”,但是可以使用。谢谢! (2认同)
  • @CameronHudson:如果您使用“静态导入”,那么它只是“入口”。 (2认同)

gre*_*561 1020

这是一种方式.

HashMap<String, String> h = new HashMap<String, String>() {{
    put("a","b");
}};
Run Code Online (Sandbox Code Playgroud)

但是,您应该小心并确保您理解上面的代码(它创建一个继承自HashMap的新类).因此,您应该在这里阅读更多内容: http ://www.c2.com/cgi/wiki?DoubleBraceInitialization,或者只是使用Guava:

Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
Run Code Online (Sandbox Code Playgroud)

  • 是的,这是我写的关于小心并给出描述链接的方式. (92认同)
  • 它有效,但它很丑陋并且具有用户在做之前应该理解的无形副作用 - 例如,在现场生成一个完整的匿名类. (71认同)
  • 你可以添加"as ImmutableMap.builder.put("k1","v1").put("k2","v2").build()"因为"of"方法最多限制为5对? (17认同)
  • 很棒的链接.该链接中的参考[GreencoddsTenthRuleOfProgramming](http://www.c2.com/cgi/wiki?GreencoddsTenthRuleOfProgramming)值得一读. (6认同)
  • + 对于番石榴 http://google.github.io/guava/releases/18.0/api/docs/com/google/common/collect/ImmutableMap.html (3认同)
  • 2016 年。我一直在谷歌上搜索同样的问题并得到同样的答案。希望我每次都能投票.. (2认同)

Jen*_*ann 333

如果您允许第三方库,您可以使用GuavaImmutableMap来实现类似文字的简洁:

Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
Run Code Online (Sandbox Code Playgroud)

这适用于最多5个键/值对,否则您可以使用其构建器:

Map<String, String> test = ImmutableMap.<String, String>builder()
    .put("k1", "v1")
    .put("k2", "v2")
    ...
    .build();
Run Code Online (Sandbox Code Playgroud)


  • 请注意,Guava的ImmutableMap实现与Java的HashMap实现不同(最值得注意的是它是不可变的并且不允许空键/值)
  • 有关更多信息,请参阅Guava关于其不可变集合类型的用户指南文章

  • 另外,guava有ImmutableMap.builder.put("k1","v1").put("k2","v2").build(); (26认同)
  • ImmutableMap与HashMap不同,因为它会在空值上失败,而地图HashMap则不会. (16认同)
  • 只是为了帮助可能会遇到此问题的其他人。您必须键入构建器以使其成为Map &lt;String,String&gt;,如下所示:Map &lt;String,String&gt; test = ImmutableMap。&lt;String,String&gt; builder()。put(“ k1”,“ v1”)。 put(“ k2”,“ v2”)。build(); (2认同)

Paŭ*_*ann 103

没有直接的方法可以做到这一点 - Java没有Map文字(但我认为它们是针对Java 8提出的).

有些人喜欢这样:

Map<String,String> test = new HashMap<String, String>(){{
       put("test","test"); put("test","test");}};
Run Code Online (Sandbox Code Playgroud)

这将创建一个HashMap的匿名子类,其实例初始化程序会放置这些值.(顺便说一句,地图不能包含两倍相同的值,你的第二个put会覆盖第一个.我会为下一个例子使用不同的值.)

正常的方式是这个(对于局部变量):

Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");
Run Code Online (Sandbox Code Playgroud)

如果您的testmap是实例变量,请将初始化放在构造函数或实例初始值设定项中:

Map<String,String> test = new HashMap<String, String>();
{
    test.put("test","test");
    test.put("test1","test2");
}
Run Code Online (Sandbox Code Playgroud)

如果您的test映射是类变量,请将初始化放在静态初始化程序中:

static Map<String,String> test = new HashMap<String, String>();
static {
    test.put("test","test");
    test.put("test1","test2");
}
Run Code Online (Sandbox Code Playgroud)

如果您希望地图永远不会更改,则应在初始化后将地图包裹起来Collections.unmodifiableMap(...).您也可以在静态初始化程序中执行此操作:

static Map<String,String> test;
{
    Map<String,String> temp = new HashMap<String, String>();
    temp.put("test","test");
    temp.put("test1","test2");
    test = Collections.unmodifiableMap(temp);
}
Run Code Online (Sandbox Code Playgroud)

(我不确定你现在能否做出test决定......试试看并在这里报道.)


Sha*_*rog 61

Map<String,String> test = new HashMap<String, String>()
{
    {
        put(key1, value1);
        put(key2, value2);
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 但是应该注意记忆含义.https://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/ (15认同)
  • 我喜欢它的整洁,但它创建了不必要的匿名类,并且有这里描述的问题:http://www.c2.com/cgi/wiki?DoubleBraceInitialization (2认同)

小智 42

另一种方法是使用普通的Java 7类和varargs:HashMapBuilder使用此方法创建一个类:

public static HashMap<String, String> build(String... data){
    HashMap<String, String> result = new HashMap<String, String>();

    if(data.length % 2 != 0) 
        throw new IllegalArgumentException("Odd number of arguments");      

    String key = null;
    Integer step = -1;

    for(String value : data){
        step++;
        switch(step % 2){
        case 0: 
            if(value == null)
                throw new IllegalArgumentException("Null key value"); 
            key = value;
            continue;
        case 1:             
            result.put(key, value);
            break;
        }
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

使用这样的方法:

HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");
Run Code Online (Sandbox Code Playgroud)

  • Apache Utils 的另一个解决方案从未提及,但可读,使用以前的 Java 版本: MapUtils.putAll(new HashMap&lt;String, String&gt;(), new Object[] { "My key", "my value", ... (2认同)

Joh*_*ler 9

爪哇 8

在普通的 Java 8 中,您也可以使用它Streams/Collectors来完成这项工作。

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", "value2"),
         new SimpleEntry<>("key3", "value3"))
        .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue));
Run Code Online (Sandbox Code Playgroud)

这具有不创建匿名类的优点。

请注意,进口是:

import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;
Run Code Online (Sandbox Code Playgroud)

当然,正如其他答案中所述,在 Java 9 之后,您可以使用更简单的方法来做同样的事情。


Bea*_*low 7

我想对约翰尼·威勒的回答提出一个简短的警告。

Collectors.toMap依赖Map.merge且不期望 null 值,因此它将抛出 a,NullPointerException如本错误报告中所述: https: //bugs.openjdk.java.net/browse/JDK-8148463

另外,如果一个键出现多次,默认情况下Collectors.toMap会抛出一个IllegalStateException.

在 Java 8 上使用构建器语法获取具有空值的映射的另一种方法是编写由 HashMap 支持的自定义收集器(因为它确实允许空值):

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", (String) null),
         new SimpleEntry<>("key3", "value3"),
         new SimpleEntry<>("key1", "value1updated"))
        .collect(HashMap::new,
                (map, entry) -> map.put(entry.getKey(),
                                        entry.getValue()),
                HashMap::putAll);
Run Code Online (Sandbox Code Playgroud)


Bas*_*que 5

tl;博士

使用Map.of…Java 9 及更高版本中的方法。

Map< String , String > animalSounds =
    Map.of(
        "dog"  , "bark" ,   // key , value
        "cat"  , "meow" ,   // key , value
        "bird" , "chirp"    // key , value
    )
;
Run Code Online (Sandbox Code Playgroud)

Map.of

Java 9 添加了一系列Map.of静态方法来做你想做的事:使用文字语法实例化一个不可变的Map

映射(条目的集合)是不可变的,因此您无法在实例化后添加或删除条目。此外,每个条目的键和值是不可变的,无法更改。其他规则请参见 Javadoc,例如不允许 NULL,不允许重复键,映射的迭代顺序是任意的。

让我们看看这些方法,使用一些样本数据绘制一周中某天到我们期望在那天工作的人的地图。

Person alice = new Person( "Alice" );
Person bob = new Person( "Bob" );
Person carol = new Person( "Carol" );
Run Code Online (Sandbox Code Playgroud)

Map.of()

Map.of创建一个空的Map. 不可修改,因此您无法添加条目。这是此类地图的示例,为空且没有条目。

Map < DayOfWeek, Person > dailyWorkerEmpty = Map.of();
Run Code Online (Sandbox Code Playgroud)

DailyWorkerEmpty.toString(): {}

Map.of( … )

Map.of( k , v , k , v , …)有几种方法需要 1 到 10 个键值对。这是两个条目的示例。

Map < DayOfWeek, Person > weekendWorker = 
        Map.of( 
            DayOfWeek.SATURDAY , alice ,     // key , value
            DayOfWeek.SUNDAY , bob           // key , value
        )
;
Run Code Online (Sandbox Code Playgroud)

weekendWorker.toString(): {SUNDAY=Person{ name='Bob' }, SATURDAY=Person{ name='Alice' }}

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … )接受任意数量的实现Map.Entry接口的对象。Java 捆绑了两个实现该接口的类,一个是可变的,另一个是不可变的: AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry. 但是我们不需要指定一个具体的类。我们只需要调用Map.entry( k , v )方法,传递我们的键和我们的值,我们就会返回一个实现Map.Entry接口的类的对象。

Map < DayOfWeek, Person > weekdayWorker = Map.ofEntries(
        Map.entry( DayOfWeek.MONDAY , alice ) ,            // Call to `Map.entry` method returns an object implementing `Map.Entry`. 
        Map.entry( DayOfWeek.TUESDAY , bob ) ,
        Map.entry( DayOfWeek.WEDNESDAY , bob ) ,
        Map.entry( DayOfWeek.THURSDAY , carol ) ,
        Map.entry( DayOfWeek.FRIDAY , carol )
);
Run Code Online (Sandbox Code Playgroud)

weekdayWorker.toString(): {WEDNESDAY=Person{ name='Bob' }, TUESDAY=Person{ name='Bob' }, THURSDAY=Person{ name='Carol' }, FRIDAY=Person{ name='Carol' } , MONDAY=Person{ name='Alice' }}

Map.copyOf

Java 10 添加了该方法Map.copyOf。传递现有地图,取回该地图的不可变副本。

笔记

注意,图的迭代顺序产生经由Map.of保证。条目具有任意顺序。不要根据看到的顺序编写代码,因为文档警告顺序可能会发生变化。

请注意,所有的这些Map.of…方法返回Map未指定类别的。底层的具体类甚至可能因 Java 的一个版本而异。这种匿名性使 Java 能够从各种实现中进行选择,无论哪种最适合您的特定数据。例如,如果您的键来自enum,Java 可能会在幕后使用EnumMap