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

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

正确的语法是什么?我没有发现任何与此相关的信息。这可能吗?我正在寻找最短/最快的方法来将一些“最终/静态”值放在地图中,这些值永远不会改变,并且在创建地图时预先知道。


当前回答

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

Collectors.toMap依赖于Map.merge,不需要null值,因此它将抛出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);

其他回答

我们可以使用具有SimpleEntry的AbstractMap类,该类允许创建不可变映射

Map<String, String> map5 = Stream.of(
    new AbstractMap.SimpleEntry<>("Sakshi","java"),
    new AbstractMap.SimpleEntry<>("fine","python")
    ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    
    System.out.println(map5.get("Sakshi"));
    map5.put("Shiva", "Javascript");
    System.out.println(map5.get("Shiva"));// here we can add multiple entries.

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

所有版本

如果您恰好需要一个条目:Collections.singletonMap(“key”,“value”)。

对于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:
import static java.util.Map.entry;    
Map<String, String> test2 = Map.ofEntries(
    entry("a", "b"),
    entry("c", "d")
);

在上面的示例中,test和test2都是相同的,只是表示Map的方式不同。Map.of方法最多为映射中的十个元素定义,而Map.ofEntries方法没有这样的限制。

注意,在这种情况下,生成的映射将是一个不可变的映射。如果希望映射是可变的,可以再次复制它,例如使用mutableMap=newHashMap<>(map.of(“a”,“b”));。还要注意,在这种情况下,键和值不能为空。

(另见JEP 269和Javadoc)

对于Java版本8之前的版本:

否,您必须手动添加所有元素。您可以在匿名子类中使用初始值设定项,使语法稍短:

Map<String, String> myMap = new HashMap<String, String>() {{
    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;
}

不幸的是,如果键和值的类型不同,则使用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;
  }
}

并像使用新的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;
  }
}

并像新的MapBuilder<String,Object>().put(“a”,1).pput(“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=Map.of(p(“a”,1),p(“b”,“foo”);

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

以下代码可以在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]));

信用: