ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

密切相关:stackoverflow.com/questions/507602/…(两个问题都是关于使用静态最终值初始化常量映射。)
不是 the linked Question 的副本。该问题是关于 static 的,而该问题是关于通过文字语法进行实例化的。 投票重新开放。 也许这个问题与其他问题重复;如果是这样,请通过链接到真正原创的问题重新打开和关闭。

F
Fangming

所有版本

如果您碰巧只需要一个条目:有 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")
);

在上面的示例中,testtest2 都是相同的,只是表示 Map 的方式不同。 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");
}};

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

它生成一个额外的类,增加内存消耗、磁盘空间消耗和启动时间

在非静态方法的情况下:它包含对调用创建方法的对象的引用。这意味着当创建的映射对象仍然被引用时,外部类的对象不能被垃圾回收,从而阻塞了额外的内存

使用初始化函数还可以让您在初始化程序中生成映射,但避免了令人讨厌的副作用:

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

如果您想初始化函数中的元素,这将不起作用......
@Michael:是的,如果你想使用一个函数而不是你不能使用一个非函数。但是你为什么想要?
对于需要带有单个条目的 Map 的情况,有 Collections.singletonMap() :)
现在稳定的 Java 9 已经发布,我更喜欢 this link for Javadoc。 +1 因为少了一个依赖!
Java 9 entry 记录在哪里?
D
Druckles

这是一种方式。

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 个条目。否则,请使用构建器:source


它可以工作,但它很丑陋并且具有用户在执行此操作之前应该了解的无形副作用 - 例如,当场生成整个匿名类。
是的,这就是我写的关于小心的方式,并给出了描述的链接。
很棒的链接。该链接中对 GreencoddsTenthRuleOfProgramming 的引用值得一读。
你可以添加 "as ImmutableMap.builder.put("k1","v1").put("k2","v2").build()" 因为 "of" 方法最多限制为 5 对?
J
Jens Hoffmann

如果您允许 3rd 方库,则可以使用 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 的关于其不可变集合类型的用户指南文章


此外,番石榴有 ImmutableMap.builder.put("k1","v1").put("k2","v2").build();
ImmutableMap 与 HashMap 不同,因为它会在 null 值上失败,而 map HashMap 不会。
只是为了帮助其他可能面临这个问题的人。您必须键入构建器以使其成为 Map,如下所示:Map test = ImmutableMap.builder().put("k1", "v1")。 put("k2", "v2").build();
这是真棒詹斯!
P
Paŭlo Ebermann

没有直接的方法可以做到这一点 - 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 定为最终版本……试试看并在此处报告。)


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

简单明了。我认为这与扩展评论部分将是最好的答案。
但是,应该注意内存方面的影响。 blog.jooq.org/2014/12/08/…
@Amalgovinus 基本上,通过创建一个新的子类,您将 HashMap 中的类型参数硬编码到这个子类中。这只有在您实际提供它们时才有效。 (对于一个新的(空的)HashMap,类型参数是不相关的。)
我喜欢它的简洁性,但它会创建不必要的匿名类并且存在此处描述的问题:c2.com/cgi/wiki?DoubleBraceInitialization
@hello_its_me:因为它与 stackoverflow.com/a/6802512/1386911 答案相同,只是格式不同。在这种情况下,这种扩展格式在紧凑格式的可读性之上没有额外的价值。
A
Aerthel

另一种方法是使用普通的 Java 7 类和可变参数:使用此方法创建一个类 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");

我写了一个受您启发的答案:stackoverflow.com/questions/507602/…
另一个从未提及但可读的 Apache Utils 解决方案,使用以前的 Java 版本: MapUtils.putAll(new HashMap(), new Object[] { "My key", "my value", ...
一个优雅且易于理解的示例! Apache Utils 也很棒。
J
Johnny Willer

爪哇 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 及以后的版本中,您可以使用更简单的方法来做同样的事情。


如果您使用的是 apache commons,您也可以使用 Pair.of() 而不是 new SimpleEntry<>,然后使用 Pair::getKeyPair::getValue
B
Basil Bourque

tl;博士

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

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

地图.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
        )
;

周末工人.toString(): {SUNDAY=Person{ name='Bob' }, SATURDAY=Person{ 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=Person{ name='Bob' }, TUESDAY=Person{ name='Bob' }, THURSDAY=Person{ name='Carol' }, FRIDAY=Person{ name='Carol' } , 星期一=人{名称='爱丽丝'}}

Map.copyOf

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

笔记

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

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


A
Anuj Dhiman

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

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

使用 Collections Map immutableMap = Collections.singletonMap("rohit", "cricket") 的单例 HashMaps;使用 Collections Map emptyMap = Collections.emptyMap(); 的空 HashMaps创建 HashMap 的匿名子类 Map map = new HashMap() {{ put("hcl", "amit");放(“tcs”,“ravi”);放(“wipro”,“anmol”); }};


B
BeardOverflow

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

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

entry(...)new SimpleEntry<>(...) 更易于使用(导入 java.util.Map.entry
R
Ruli

我们可以使用具有 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.

G
Gerrit Schulz

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

N
NotNV6

您可以通过 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)));
    }};
}

注意不建议对所有内容都使用它,因为每次使用它都会生成一个匿名类。


B
Balakrishna Kudikala

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


F
Furkan Yavuz

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

M
Milton Bertachini

执行此操作的简单方法:

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

这不只是最流行的答案中描述的不受欢迎的旧 Java9 之前方法的变体吗?
e
esotericpig

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

J
JHH

不幸的是,如果键和值的类型不同,则使用可变参数不是很合理,因为您必须使用 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");

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


R
Robocide

您可以在 Java 8 中使用 Streams(这是 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


P
PaulNUK

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

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

  //populate ret
  ...

  return ret;
}

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

n
nurb

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


u
user16547619

Map.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(..) 构造函数创建的映射不允许键和值都是 null


a
avp

我发现 a great article by baeldung 列出了在不同 Java 版本中执行此操作的几种方法。

一些可以方便的有趣方法是

对于任何 Java 版本

public static Map<String, String> articleMapOne;
static {
    articleMapOne = new HashMap<>();
    articleMapOne.put("ar01", "Intro to Map");
    articleMapOne.put("ar02", "Some article");
}

对于使用流的 Java 8

Map<String, String> map = Stream.of(new String[][] {
  { "Hello", "World" }, 
  { "John", "Doe" }, 
}).collect(Collectors.toMap(data -> data[0], data -> data[1]));