将 HashMap 扩展为 return 空 HashSet 用于未找到的键
Extend HashMap to return empty HashSet for non-found keys
我想制作一个包含 HashSet 作为值的 HashMap,并且 returns 在找不到键时包含一个空的 HashSet。
public class IsbnHashMap<K,V> extends HashMap<K,V> {
protected V defaultValue;
public IsbnHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}
但是我的实现不起作用。
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>();
这个returns"HashSet cannot be applied"。如果我尝试将 IsbnHashMap
中的 K,V
更改为 <String, HashSet<String>>
,我也会遇到一些奇怪的错误。我该如何实施?
正如 Jon Skeet 所说...
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>(new HashSet<String>());
...但是,这将 return 与 Dunni 指出的相同的默认对象。
这样就可以了:
private static HashMap<String, HashSet<String>> isbnToId = new HashMap<String, HashSet<String>>();
public static void coupleIsbnToId(String isbn, String _id) {
if (!isbnToId.containsKey(isbn)) {
isbnToId.put(isbn, new HashSet<String>());
}
isbnToId.get(isbn).add(_id);
}
首先需要注意的是,在Java-8中你可以改用:
isbnToId.computeIfAbsent(isbn, k -> new HashSet<>()).add(_id);
其次,如果你真的想在以前的 Java 版本中做这样的事情,你最好为此目的创建单独的方法(例如,getOrDefault()
),以免违约。第三,您需要为每个新密钥创建 new HashSet<>()
。如果您 return 同一个实例,它将在给定的密钥之间共享。如果您不希望用户修改它,最好使用 unmodifiable Collections.emptySet()
作为默认值。这样用户可以安全地执行 isbnToId.getOrDefault(isbn).contains(_id)
,但尝试 isbnToId.getOrDefault(isbn).add(_id)
将导致异常。如果你想支持修改(在 Java-8 之前),你可以,例如,将元素 class 传递给构造函数:
public static class MyMap<K, V> extends HashMap<K, V> {
private Class<?> clazz;
public MyMap(Class<?> clazz) {
this.clazz = clazz;
}
public V getOrCompute(K key) {
V v = get(key);
if(v == null) {
try {
v = (V) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
put(key, v);
}
return v;
}
}
用法示例:
MyMap<String, Set<String>> map = new MyMap<>(HashSet.class);
map.getOrCompute("a").add("b");
map.getOrCompute("a").add("c");
map.getOrCompute("d").add("e");
System.out.println(map); // {a=[b, c], d=[e]}
这里我们假设用默认构造函数实例化传递的class是可以的。另一种方法是传递能够生成默认值的工厂接口。
我想制作一个包含 HashSet 作为值的 HashMap,并且 returns 在找不到键时包含一个空的 HashSet。
public class IsbnHashMap<K,V> extends HashMap<K,V> {
protected V defaultValue;
public IsbnHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}
但是我的实现不起作用。
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>();
这个returns"HashSet cannot be applied"。如果我尝试将 IsbnHashMap
中的 K,V
更改为 <String, HashSet<String>>
,我也会遇到一些奇怪的错误。我该如何实施?
正如 Jon Skeet 所说...
private static IsbnHashMap<String, HashSet<String>> isbnToId = new IsbnHashMap<String, HashSet<String>>(new HashSet<String>());
...但是,这将 return 与 Dunni 指出的相同的默认对象。
这样就可以了:
private static HashMap<String, HashSet<String>> isbnToId = new HashMap<String, HashSet<String>>();
public static void coupleIsbnToId(String isbn, String _id) {
if (!isbnToId.containsKey(isbn)) {
isbnToId.put(isbn, new HashSet<String>());
}
isbnToId.get(isbn).add(_id);
}
首先需要注意的是,在Java-8中你可以改用:
isbnToId.computeIfAbsent(isbn, k -> new HashSet<>()).add(_id);
其次,如果你真的想在以前的 Java 版本中做这样的事情,你最好为此目的创建单独的方法(例如,getOrDefault()
),以免违约。第三,您需要为每个新密钥创建 new HashSet<>()
。如果您 return 同一个实例,它将在给定的密钥之间共享。如果您不希望用户修改它,最好使用 unmodifiable Collections.emptySet()
作为默认值。这样用户可以安全地执行 isbnToId.getOrDefault(isbn).contains(_id)
,但尝试 isbnToId.getOrDefault(isbn).add(_id)
将导致异常。如果你想支持修改(在 Java-8 之前),你可以,例如,将元素 class 传递给构造函数:
public static class MyMap<K, V> extends HashMap<K, V> {
private Class<?> clazz;
public MyMap(Class<?> clazz) {
this.clazz = clazz;
}
public V getOrCompute(K key) {
V v = get(key);
if(v == null) {
try {
v = (V) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
put(key, v);
}
return v;
}
}
用法示例:
MyMap<String, Set<String>> map = new MyMap<>(HashSet.class);
map.getOrCompute("a").add("b");
map.getOrCompute("a").add("c");
map.getOrCompute("d").add("e");
System.out.println(map); // {a=[b, c], d=[e]}
这里我们假设用默认构造函数实例化传递的class是可以的。另一种方法是传递能够生成默认值的工厂接口。