有没有这样初始化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)));
    }};
}

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

其他回答

of()似乎最普遍,也最不受限制。这里,它自动处理非对象输入值:

List<Map<String, Object> certs = new ArrayList<>(){{ add( Map.of(
    "TAG",          Obj1 // Object
    "TAGGED_ID",    1L //Long
    "DEGREE",       "PARENT" // String
    "DATE",         LocalDate.now() // LocalDate
));}};

注意,由静态Map.of(..)构造函数创建的映射不允许键和值都为空。

没有直接的方法可以做到这一点——截至2021,Java没有Map文字(然而,我认为它们是为Java8提出的,但没有实现)。

有些人喜欢这样:

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

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

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

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

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

如果你希望你的映射永远不变,你应该在初始化后用Collections.unmodifiedMap(…)包装你的映射。你也可以在静态初始化器中这样做:

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

(我不确定你现在是否能让考试成为最终……试试看,然后在这里报告。)

从Java9开始,您还拥有Map.of(…)和Map.entries()语法,正如yankee的回答所解释的。

另一种方法是使用纯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");

如果它是一个实例变量,那么一个实例初始化块绝对是最好的方法,特别是如果你不能使用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);
    });

我们使用一个简单的实用程序类以流畅的方式初始化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;
    }
}