有没有办法像这样初始化Java HashMap?
Map<String,String> test =
new HashMap<String, String>{"test":"test","test":"test"};
正确的语法是什么?我还没有发现任何有关此的信息。这可能吗?我正在寻找在地图中放置一些“最终/静态”值的最短/最快方法,这些值永远不会改变,并且在创建地图时会事先知道。
有没有办法像这样初始化Java HashMap?
Map<String,String> test =
new HashMap<String, String>{"test":"test","test":"test"};
正确的语法是什么?我还没有发现任何有关此的信息。这可能吗?我正在寻找在地图中放置一些“最终/静态”值的最短/最快方法,这些值永远不会改变,并且在创建地图时会事先知道。
Answers:
如果您碰巧只需要一个条目:有Collections.singletonMap("key", "value")
。
是的,现在有可能。在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.of
最多可为地图中的十个元素定义该方法,而该Map.ofEntries
方法没有此限制。
请注意,在这种情况下,生成的地图将是不可变的地图。如果您希望地图可变,则可以再次复制它,例如使用mutableMap = new HashMap<>(Map.of("a", "b"));
不,您将必须手动添加所有元素。您可以在匿名子类中使用初始化程序,以使语法短一些:
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;
}
Collections.singletonMap()
:)
entry
记录?
这是一种方式。
HashMap<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);
如果您允许第3方库,则可以使用Guava的ImmutableMap达到字面意义的简洁性:
Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
Map<String, String> test = ImmutableMap.<String, String>builder()
.put("k1", "v1")
.put("k2", "v2")
...
.build();
没有直接的方法可以做到-Java没有Map文字(但是-我认为它们是为Java 8提出的)。
有些人喜欢这样:
Map<String,String> test = new HashMap<String, String>(){{
put("test","test"); put("test","test");}};
这将创建HashMap的匿名子类,其实例初始化程序将这些值放入。(顺便说一句,地图不能包含相同值的两倍,您的第二个放置权将覆盖第一个放置。在下一个示例中,我将使用不同的值。)
正常的方式是这样的(对于局部变量):
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类和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 9及更高版本中的方法。
Map< String , String > animalSounds =
Map.of(
"dog" , "bark" , // key , value
"cat" , "meow" , // key , value
"bird" , "chirp" // key , value
)
;
Map.of
Java 9添加了一系列Map.of
静态方法来完成您想做的事情:使用文字语法实例化不可变的东西Map
。
映射(条目的集合)是不可变的,因此您无法在实例化之后添加或删除条目。而且,每个条目的键和值都是不可变的,无法更改。有关其他规则,请参见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
)
;
WeekendWorker.toString():{SUNDAY =人{name ='Bob'},SATURDAY =人{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'} ,MONDAY = Person {name ='Alice'}}
Map.copyOf
Java 10添加了该方法Map.copyOf
。传递现有地图,取回该地图的不变副本。
注意,图的迭代顺序产生经由Map.of
被不保证。条目具有任意顺序。不要根据所看到的顺序来编写代码,因为文档警告该顺序可能会更改。
请注意,所有的这些Map.of…
方法返回Map
的未指定类别的。底层的具体类甚至可能从一个Java版本到另一个Java版本都不同。这种匿名性使Java可以从各种实现中进行选择,无论哪种方式都可以最适合您的特定数据。例如,如果您的密钥来自枚举,则Java可能会在幕后使用EnumMap
。
您可以通过Map.of
2种简单的方法轻松地制作自己的方法(仅在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)));
}};
}
注意 不建议将其用于所有内容,因为每次使用时都会创建一个匿名类。
JAVA 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及更高版本中,您可以使用更简单的方法来进行此操作。
不幸的是,如果键和值的类型不同,则使用varargs不太合理,因为您将不得不使用Object...
并且完全失去类型安全性。如果您总是要创建例如a Map<String, String>
,则当然可以创建a ,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");
(而不是Pair
人们想象的使用Map.Entry
,但是由于它是一个接口,因此它需要一个实现类和/或一个辅助工厂方法。它也不是不变的,还包含其他对该任务无用的逻辑。)
您可以在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