有没有像这样初始化 Java HashMap 的方法?:
Map<String,String> test =
new HashMap<String, String>{"test":"test","test":"test"};
什么是正确的语法?我没有发现任何关于此的信息。这可能吗?我正在寻找最短/最快的方法将一些“最终/静态”值放入地图中,这些值永远不会改变并且在创建地图时提前知道。
static
的,而该问题是关于通过文字语法进行实例化的。 投票重新开放。 也许这个问题与其他问题重复;如果是这样,请通过链接到真正原创的问题重新打开和关闭。
所有版本
如果您碰巧只需要一个条目:有 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 = new HashMap<>(Map.of("a", "b"));
对于高达 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 个条目。否则,请使用构建器:source。
如果您允许 3rd 方库,则可以使用 Guava 的 ImmutableMap 来实现类似文字的简洁:
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 的关于其不可变集合类型的用户指南文章
没有直接的方法可以做到这一点 - 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
定为最终版本……试试看并在此处报告。)
Map<String,String> test = new HashMap<String, String>()
{
{
put(key1, value1);
put(key2, value2);
}
};
HashMap
中的类型参数硬编码到这个子类中。这只有在您实际提供它们时才有效。 (对于一个新的(空的)HashMap,类型参数是不相关的。)
另一种方法是使用普通的 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");
爪哇 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 及以后的版本中,您可以使用更简单的方法来做同样的事情。
Pair.of()
而不是 new SimpleEntry<>
,然后使用 Pair::getKey
和 Pair::getValue
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.SimpleEntry
、AbstractMap.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
。
我们可以使用以下方式初始化 hashmap:
HashMap 使用构造函数 Map
Hashmap 有四种不同类型的构造函数,因此我们可以根据需要对其进行初始化。现在使用 HashMap(int initialCapacity) 构造函数
Map<String, String> hashMap = new HashMap<String, String>(3);
hashMap.put("virat", "cricket");
hashMap.put("amit","football");
使用 Collections Map
我想对约翰尼威勒的回答提出一个简短的警告。
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
)
我们可以使用具有 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.
我们使用一个简单的实用程序类以流畅的方式初始化 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;
}
}
您可以通过 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)));
}};
}
注意不建议对所有内容都使用它,因为每次使用它都会生成一个匿名类。
如果只需要放置一个键值对,可以使用 Collections.singletonMap(key, value);
使用 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");
执行此操作的简单方法:
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;
}
如果它是一个实例变量,那么实例初始化块绝对是要走的路,特别是如果你不能使用 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);
});
不幸的是,如果键和值的类型不同,则使用可变参数不是很合理,因为您必须使用 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
,但由于它是一个接口,它需要一个实现类和/或一个辅助工厂方法。它也不是不可变的,并且包含其他对此任务无用的逻辑。)
您可以在 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
您可以创建一个方法来初始化地图,如下面的示例所示:
Map<String, Integer> initializeMap()
{
Map<String, Integer> ret = new HashMap<>();
//populate ret
...
return ret;
}
//call
Map<String, Integer> map = initializeMap();
以下代码可以在 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]));
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 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]));
不定期副业成功案例分享
Collections.singletonMap()
:)entry
记录在哪里?