返回未找到键的默认值?

有没有可能让 HashMap为集合中没有找到的所有键返回一个默认值?

190530 次浏览

它默认执行此操作,返回 null

不能直接使用,但是可以扩展该类以修改其 get 方法。下面是一个现成的示例: http://www.java2s.com/Code/Java/Collections-Data-Structure/ExtendedVersionofjavautilHashMapthatprovidesanextendedgetmethodaccpetingadefaultvalue.htm

[更新]

正如其他回答者和评论者所指出的,从 Java8开始,您可以简单地调用 Map#getOrDefault(...)

[原创]

目前还没有完全做到这一点的 Map 实现,但是通过扩展 HashMap 来实现您自己的实现是微不足道的:

public class DefaultHashMap<K,V> extends HashMap<K,V> {
protected V defaultValue;
public DefaultHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}

如果你不喜欢重造轮子,可以使用 Commons 的 默认地图,例如:

Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname");
// surname == "[NO ENTRY FOUND]"

如果一开始不负责创建地图,也可以传入现有的地图。

您可以简单地创建一个继承 HashMap 的新类并添加 getDefault 方法。 下面是一个示例代码:

public class DefaultHashMap<K,V> extends HashMap<K,V> {
public V getDefault(K key, V defaultValue) {
if (containsKey(key)) {
return get(key);
}


return defaultValue;
}
}

我认为您不应该在您的实现中覆盖 get (K 键)方法,因为 Ed Staub 在他的评论中指定的原因,并且因为您将破坏 Map 接口的 合约(这可能导致一些难以找到的 bug)。

/**
* Extension of TreeMap to provide default value getter/creator.
*
* NOTE: This class performs no null key or value checking.
*
* @author N David Brown
*
* @param <K>   Key type
* @param <V>   Value type
*/
public abstract class Hash<K, V> extends TreeMap<K, V> {


private static final long serialVersionUID = 1905150272531272505L;


/**
* Same as {@link #get(Object)} but first stores result of
* {@link #create(Object)} under given key if key doesn't exist.
*
* @param k
* @return
*/
public V getOrCreate(final K k) {
V v = get(k);
if (v == null) {
v = create(k);
put(k, v);
}
return v;
}


/**
* Same as {@link #get(Object)} but returns specified default value
* if key doesn't exist. Note that default value isn't automatically
* stored under the given key.
*
* @param k
* @param _default
* @return
*/
public V getDefault(final K k, final V _default) {
V v = get(k);
return v == null ? _default : v;
}


/**
* Creates a default value for the specified key.
*
* @param k
* @return
*/
abstract protected V create(final K k);
}

示例用法:

protected class HashList extends Hash<String, ArrayList<String>> {
private static final long serialVersionUID = 6658900478219817746L;


@Override
public ArrayList<Short> create(Short key) {
return new ArrayList<Short>();
}
}


final HashList haystack = new HashList();
final String needle = "hide and";
haystack.getOrCreate(needle).add("seek")
System.out.println(haystack.get(needle).get(0));

在 Java8中,使用 Map.getOrDefault。它获取键,如果没有找到匹配的键,则返回值。

你就不能创建一个静态方法来完成这个任务吗?

private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
return map.containsKey(key) ? map.get(key) : defaultValue;
}

Java8为 Map接口引入了一个不错的 没有默认方法,它存储延迟计算值,因此不会破坏 map 契约:

Map<Key, Graph> map = new HashMap<>();
map.computeIfAbsent(aKey, key -> createExpensiveGraph(key));

产地: http://blog.javabien.net/2014/02/20/loadingcache-in-java-8-without-guava/

免责声明: 这个答案并不完全符合 OP 的要求,但在某些情况下,当键数有限且缓存不同的值时,匹配问题的标题可能会很方便。它不应该在相反的情况下使用大量的键和相同的默认值,因为这将不必要地浪费内存。

我需要阅读从 JSON 的一个服务器返回的结果,在那里我不能保证字段会出现。我使用的是从 HashMap 派生的 org.json.simple.JSONObject 类。以下是我使用的一些辅助功能:

public static String getString( final JSONObject response,
final String key )
{ return getString( response, key, "" ); }
public static String getString( final JSONObject response,
final String key, final String defVal )
{ return response.containsKey( key ) ? (String)response.get( key ) : defVal; }


public static long getLong( final JSONObject response,
final String key )
{ return getLong( response, key, 0 ); }
public static long getLong( final JSONObject response,
final String key, final long defVal )
{ return response.containsKey( key ) ? (long)response.get( key ) : defVal; }


public static float getFloat( final JSONObject response,
final String key )
{ return getFloat( response, key, 0.0f ); }
public static float getFloat( final JSONObject response,
final String key, final float defVal )
{ return response.containsKey( key ) ? (float)response.get( key ) : defVal; }


public static List<JSONObject> getList( final JSONObject response,
final String key )
{ return getList( response, key, new ArrayList<JSONObject>() ); }
public static List<JSONObject> getList( final JSONObject response,
final String key, final List<JSONObject> defVal ) {
try { return response.containsKey( key ) ? (List<JSONObject>) response.get( key ) : defVal; }
catch( ClassCastException e ) { return defVal; }
}

用途:

myHashMap.getOrDefault(key, defaultValue);

我发现 LazyMap很有用。

当使用映射中不存在的键调用 get (Object)方法时,将使用工厂来创建对象。创建的对象将使用请求的键添加到映射中。

这允许你这样做:

    Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
map.get(notExistingKey).incrementAndGet();

get的调用为给定的键创建一个默认值。指定如何使用 LazyMap.lazyMap(map, factory)的工厂参数创建默认值。在上面的示例中,映射被初始化为一个值为0的新 AtomicInteger

在 Java/Kotlin 混合项目中也考虑 Kotlin 的 默认地图

参见 从 Java 访问 Kotlin 扩展函数

在 java 8 + 上

Map.getOrDefault(Object key,V defaultValue)
    public final Map<String, List<String>> stringMap = new ConcurrentHashMap<String, List<String>>() {
@Nullable
@Override
public List<String> get(@NonNull Object key) {
return computeIfAbsent((String) key, s -> new ArrayList<String>());
}
};

HashMap 会导致死循环,所以使用 ConcurrentHashMap 代替 HashMap,

在地图使用 GetOrDefault方法,如果 找不到密钥,我们可以把默认值在 右侧参数

通过分组 报告情况将 List 转换为 Map。

Map<String, Long> mapClaimDecisionSummaryCount = reportList.stream().collect(Collectors.groupingBy(Report::getStatus, Collectors.counting()));
Long status_approved = mapClaimDecisionSummaryCount.getOrDefault("APPROVED", 0L);
Long status_declined = mapClaimDecisionSummaryCount.getOrDefault("DECLINED", 0L);
Long status_cancelled = mapClaimDecisionSummaryCount.getOrDefault("CANCELLED", 0L);//If Key not found it takes defaultValue as 0
Long status_inprogress = mapClaimDecisionSummaryCount.getOrDefault("INPROGRESS", 0L);
      

System.out.println("APPROVED: "+ status_approved);
System.out.println("DECLINED: "+ status_declined);
System.out.println("CANCELLED: "+ status_cancelled);
System.out.println("INPROGRESS: "+ status_inprogress);

产出:

APPROVED: 20
DECLINED: 10
CANCELLED: 5
INPROGRESS: 0

正如您在上面的输出中看到的,Status进展没有找到,因此它采用默认值 0