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

时间:2011-07-23 18:40:33

标签: java dictionary collections initialization

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

Map<String,String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};

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

20 个答案:

答案 0 :(得分:1091)

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

在上面的示例中,testtest2都是相同的,只是表达地图的方式不同。 Map.of方法是为地图中最多十个元素定义的,而Map.ofEntries方法则没有此限制。

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

(另请参阅JEP 269Javadoc

对于Java版本8:

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

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

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

使用函数进行初始化也会做同样的事情,但可能会提高代码的可读性:

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

答案 1 :(得分:994)

这是一种方式。

HashMap<String, String> h = new HashMap<String, String>() {{
    put("a","b");
}};

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

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

答案 2 :(得分:320)

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

Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");

这最多可用于5 key/value pairs,否则您可以使用其builder

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


  • 请注意,Guava的ImmutableMap实现与Java的HashMap实现不同(最值得注意的是它是不可变的,并且不允许空键/值)。
  • 有关详情,请参阅Guava的immutable collection types
  • 上的用户指南文章

答案 3 :(得分:101)

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

有些人喜欢这样:

Map<String,String> test = new HashMap<String, String>(){{
       put("test","test"); put("test","test");}};

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

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

Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");

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

Map<String,String> test = new HashMap<String, String>();
{
    test.put("test","test");
    test.put("test1","test2");
}

如果您的test映射是类变量,请将初始化放在静态初始值设定项中:

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

如果您希望地图永远不会改变,您应该在初始化之后将地图包裹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);
}

(我不确定你现在是否可以让test最终......试一试并在此报告。)

答案 4 :(得分:60)

Map<String,String> test = new HashMap<String, String>()
{
    {
        put(key1, value1);
        put(key2, value2);
    }
};

答案 5 :(得分: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;
}

使用如下方法:

HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");

答案 6 :(得分:7)

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

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

请注意,导入为:

import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;

当然,如其他答案所述,在Java 9及更高版本中,您可以使用更简单的方法来做到这一点。

答案 7 :(得分:2)

我们可以使用具有 SimpleEntry 的 AbstractMap 类,它允许创建不可变的地图

set path_1="C:\Program Files\App1.exe"
set path_2="C:\Program Files\App2.exe"

CHOICE /C AB /M "choose [A] or [B]"
IF %ERRORLEVEL% EQU 2 start "%path_2%"
IF %ERRORLEVEL% EQU 1 start "%path_1%"

答案 8 :(得分:1)

您可以通过2种简单的方法轻松地制作自己的Map.of(仅在Java 9及更高版本中可用)方法

使用设置的参数量

示例

public <K,V> Map<K,V> mapOf(K k1, V v1, K k2, V v2 /* perhaps more parameters */) {
    return new HashMap<K, V>() {{
      put(k1, v1);
      put(k2,  v2);
      // etc...
    }};
}

使用列表制作

您也可以使用列表来完成此操作,而不是为某些参数集使用很多方法。

示例

public <K, V> Map<K, V> mapOf(List<K> keys, List<V> values) {
   if(keys.size() != values.size()) {
        throw new IndexOutOfBoundsException("amount of keys and values is not equal");
    }

    return new HashMap<K, V>() {{
        IntStream.range(0, keys.size()).forEach(index -> put(keys.get(index), values.get(index)));
    }};
}

注意 不建议将其用于所有内容,因为每次使用时都会创建一个匿名类。

答案 9 :(得分:1)

如果只需要放置一对键值对,则可以使用Collections.singletonMap(key,value);

答案 10 :(得分:1)

使用Java 8或更低版本

您可以使用静态块来初始化带有某些值的地图。例子:

public static Map<String,String> test = new HashMap<String, String>

static {
    test.put("test","test");
    test.put("test1","test");
}

使用Java 9或更高版本

您可以在声明时使用Map.of()方法初始化具有某些值的地图。例子:

public static Map<String,String> test = Map.of("test","test","test1","test");

答案 11 :(得分:1)

简单的方法:

public static Map<String, String> mapWithValues(String...values) {
    Map<String, String> map = new HashMap<String, String>();
    
    for (int x = 0; x < values.length; x = x+2) {
        map.put(values[x], values[x+1]);
    }
    
    return map;
}

答案 12 :(得分:1)

如果是实例变量,那么实例初始化块绝对是可行的方法,特别是如果您因为需要不同类型的映射而无法使用 Map.of() 时。

但如果您感觉活泼,可以使用 Java 8 Supplier(不推荐)。

private final Map<String,Runnable> games = ((Supplier<Map<String,Runnable>>)() -> {
  Map<String,Runnable> map = new LinkedHashMap<>();

  map.put("solarus",this::playSolarus);
  map.put("lichess",this::playLichess);

  return map;
}).get();

或者制作自己的功能界面(对我来说很好看):

@FunctionalInterface
public interface MapMaker<M> {
  static <M extends Map<K,V>,K,V> M make(M map,MapMaker<M> maker) {
    maker.build(map);
    return map;
  }

  void build(M map);
}

// Can use LinkedHashMap!
private final Map<String,Runnable> games = MapMaker.make(
    new LinkedHashMap<>(),(map) -> {
      map.put("solarus",this::playSolarus);
      map.put("lichess",this::playLichess);
    });

答案 13 :(得分:0)

不幸的是,如果键和值的类型不同,则使用varargs不太合理,因为您必须使用Object...并完全失去类型安全性。如果您始终想创建例如虽然可以使用Map<String, String>,当然可以使用toMap(String... args),但不是很漂亮,因为它很容易混淆键和值,并且奇数个参数将无效。

您可以创建HashMap的子类,该子类具有可链接的方法,例如

public class ChainableMap<K, V> extends HashMap<K, V> {
  public ChainableMap<K, V> set(K k, V v) {
    put(k, v);
    return this;
  }
}

并像new ChainableMap<String, Object>().set("a", 1).set("b", "foo")

一样使用它

另一种方法是使用常见的构建器模式:

public class MapBuilder<K, V> {
  private Map<K, V> mMap = new HashMap<>();

  public MapBuilder<K, V> put(K k, V v) {
    mMap.put(k, v);
    return this;
  }

  public Map<K, V> build() {
    return mMap;
  }
}

并像new MapBuilder<String, Object>().put("a", 1).put("b", "foo").build();

一样使用它

但是,我现在使用的解决方案然后利用了varargs和Pair类:

public class Maps {
  public static <K, V> Map<K, V> of(Pair<K, V>... pairs) {
    Map<K, V> = new HashMap<>();

    for (Pair<K, V> pair : pairs) {
      map.put(pair.first, pair.second);
    }

    return map;
  }
}

Map<String, Object> map = Maps.of(Pair.create("a", 1), Pair.create("b", "foo");

Pair.create()的冗长性让我有些困扰,但这工作得很好。如果您不介意静态导入,则当然可以创建一个助手:

public <K, V> Pair<K, V> p(K k, V v) {
  return Pair.create(k, v);
}

Map<String, Object> map = Maps.of(p("a", 1), p("b", "foo");

(代替Pair可以想象使用Map.Entry,但由于它是一个接口,因此需要实现类和/或辅助工厂方法。它也不是不变的,并且包含其他无用的逻辑为此任务。)

答案 14 :(得分:0)

您可以使用Java 8中的流(这是Set的范例):

@Test
public void whenInitializeUnmodifiableSetWithDoubleBrace_containsElements() {
    Set<String> countries = Stream.of("India", "USSR", "USA")
      .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

    assertTrue(countries.contains("India"));
}

参考:https://www.baeldung.com/java-double-brace-initialization

答案 15 :(得分:0)

tl; dr

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

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

Map.of

Java 9添加了一系列Map.of静态方法来执行您想要的操作:使用文字语法实例化不可变的Map

映射(条目的集合)是不可变的,因此您无法在实例化之后添加或删除条目。而且,每个条目的键和值都是不可变的,无法更改。 See the Javadoc用于其他规则,例如不允许NULL,不允许重复的键以及映射的迭代顺序是任意的。

让我们看一下这些方法,使用一些示例数据绘制一周中某天的地图,以此类推,我们预期该人将在这一天工作。

Person alice = new Person( "Alice" );
Person bob = new Person( "Bob" );
Person carol = new Person( "Carol" );

Map.of()

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

Map < DayOfWeek, Person > dailyWorkerEmpty = Map.of();
  

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

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

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … )接受实现Map.Entry接口的任意数量的对象。 Java将实现该接口的两个类捆绑在一起,一个是可变的,另一个是不可变的:AbstractMap.SimpleEntryAbstractMap.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 )
);
  

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

请注意, 不能保证通过Map.of生成的地图的迭代器顺序。条目具有任意顺序。不要根据所看到的顺序来编写代码,因为文档警告该顺序可能会更改。

请注意,所有这些Map.of…方法都返回{strong>一个未指定的类的Map。底层的具体类甚至可能从一个Java版本到另一个Java版本都不同。这种匿名性使Java可以从各种实现中进行选择,无论哪种方式都可以最适合您的特定数据。例如,如果您的密钥来自enum,则Java可能会在其底下使用EnumMap

答案 16 :(得分:0)

您可以创建一个方法来初始化地图,如下例所示:

Map<String, Integer> initializeMap()
{
  Map<String, Integer> ret = new HashMap<>();

  //populate ret
  ...

  return ret;
}

//call
Map<String, Integer> map = initializeMap();

答案 17 :(得分:0)

我们可以使用以下方式初始化hashmap:

  1. HashMap 使用构造函数

    Map hashMap = new HashMap();

     hashMap.put("hcl", "amit");
    
     hashMap.put("tcs","ravi");
    

Hashmap 有四种不同类型的构造函数,所以我们可以按照我们的方式初始化它 要求。现在使用 HashMap(int initialCapacity) 构造函数

    Map<String, String> hashMap = new HashMap<String, String>(3);
    hashMap.put("virat", "cricket");
    hashMap.put("amit","football");

参考:How to create HashMap

  1. 使用集合的单例 HashMap

      Map<String, String> immutableMap = Collections.singletonMap("rohit", 
           "cricket");
    
  2. 使用集合清空 HashMap

      Map<String, String> emptyMap = Collections.emptyMap();
    
  3. 用于创建 HashMap 的匿名子类

       Map<String, String> map = new HashMap<String, String>() {{
         put("hcl", "amit");
         put("tcs","ravi");
         put("wipro","anmol");
        }};
    

答案 18 :(得分:0)

根据@johnny-willer 的回答,由于 Collectors.toMap 依赖于 Map.merge,因此您无法在 Java 8 上获得具有空值的地图。此方法不期望空值,因此它会抛出一个 NullPointerException,正如此错误报告中所述:https://bugs.openjdk.java.net/browse/JDK-8148463

在 Java 8 上使用构建器语法获取空值映射的另一种方法是编写内联收集器:

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

此外,如果键出现多次,此实现将替换一个值。默认的 Collectors.toMap 没有像 (prev, next) -> next 这样的自定义合并函数,而是会抛出一个 IllegalStatException

答案 19 :(得分:0)

以下代码可以在 Java 8 中发挥作用:

Map<String, Integer> map = Stream.of(new Object[][] { 
     { "data1", 1 }, 
     { "data2", 2 }, 
 }).collect(Collectors.toMap(data -> (String) data[0], data -> (Integer) data[1]));

credits