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

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

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


当前回答

您可以通过两种简单的方式轻松地创建自己的Map.of(仅在Java9及更高版本中可用)方法

使用设定数量的参数

实例

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

笔记不建议将其用于所有对象,因为每次使用时都会生成一个匿名类。

其他回答

如果它是一个实例变量,那么一个实例初始化块绝对是最好的方法,特别是如果你不能使用Map.of(),因为你需要一个不同类型的映射。

但是,如果你感觉很活泼,你可以使用Java 8供应商(不推荐)。

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

所有版本

如果您恰好需要一个条目: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;
}

这是一种方式。

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

但是,您应该小心并确保您理解以上代码(它创建了一个继承自HashMap的新类)。因此,您应该在此处阅读更多信息:http://www.c2.com/cgi/wiki?DoubleBraceInitialization,或仅使用Guava:

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

ImmutableMap.of最多可用于5个条目。否则,使用builder:source。

我们使用一个简单的实用程序类以流畅的方式初始化Maps:

Map<String, String> map = MapInit
    .init("key1", "value1")
    .put("key2", "value2")
    .put("key3", "value3")
    .getMap();

实用程序类不受键和值的类型、条目的数量以及生成的Map的类型的限制。

实用程序类如下所示:

public class MapInit<K, V, T extends Map<K, V>> {
    private final T map;


    private MapInit(final T map) {
        this.map = map;
    }

    public T getMap() {
        return this.map;
    }

    public static <K, V> MapInit<K, V, HashMap<K, V>> init(final K key, final V value) {
        return init(HashMap::new, key, value);
    }

    public static <K, V, T extends Map<K, V>> MapInit<K, V, T> init(final Supplier<T> mapSupplier, final K key, final V value) {
        return new MapInit<>(mapSupplier.get()) //
                .put(key, value);
    }

    public MapInit<K, V, T> put(final K key, final V value) {
        this.map.put(key, value);
        return this;
    }
}

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

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

  //populate ret
  ...

  return ret;
}

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