HashMap to return default value for non-found keys?

179,229

Solution 1

[Update]

As noted by other answers and commenters, as of Java 8 you can simply call Map#getOrDefault(...).

[Original]

There's no Map implementation that does this exactly but it would be trivial to implement your own by extending 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;
  }
}

Solution 2

In Java 8, use Map.getOrDefault. It takes the key, and the value to return if no matching key is found.

Solution 3

Use Commons' DefaultedMap if you don't feel like reinventing the wheel, e.g.,

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

You can also pass in an existing map if you're not in charge of creating the map in the first place.

Solution 4

Java 8 introduced a nice computeIfAbsent default method to Map interface which stores lazy-computed value and so doesn't break map contract:

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

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

Disclamer: This answer doesn't match exactly what OP asked but may be handy in some cases matching question's title when keys number is limited and caching of different values would be profitable. It shouldn't be used in opposite case with plenty of keys and same default value as this would needlessly waste memory.

Solution 5

Can't you just create a static method that does exactly this?

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

Related videos on Youtube

Larry
Author by

Larry

Updated on May 01, 2021

Comments

  • Larry
    Larry about 3 years

    Is it possible to have a HashMap return a default value for all keys that are not found in the set?

    • SudhirJ
      SudhirJ almost 13 years
      You can check for key existence and return default. Or extend the class and modify the behavior. or even you can use null - and put some check wherever you want to use it.
    • Mark Butler
      Mark Butler over 11 years
      This is related / duplicate of stackoverflow.com/questions/4833336/… some other options are discussed there.
    • Trey Jonn
      Trey Jonn about 8 years
      Check out the Java 8 solution for Map API getOrDefault() link
  • mrkhrts
    mrkhrts almost 13 years
    @Larry, Seems a little silly to me to subclass HashMap just for this functionality when null is perfectly fine.
  • Adam Paynter
    Adam Paynter almost 13 years
    Just to be precise, you may want to adjust the condition from (v == null) to (v == null && !this.containsKey(k)) in case they purposely added a null value. I know, this is just a corner case, but the author may run into it.
  • Adam Paynter
    Adam Paynter almost 13 years
    @maerics: I noticed that you used !this.containsValue(null). This is subtly different from !this.containsKey(k). The containsValue solution will fail if some other key has been explicitly assigned a value of null. For example: map = new HashMap(); map.put(k1, null); V v = map.get(k2); In this case, v will still be null, correct?
  • Ed Staub
    Ed Staub almost 13 years
    In general, I think this is a bad idea - I'd push the defaulting behavior into the client, or a delegate that doesn't claim to be a Map. In particular, the lack of valid keySet() or entrySet() will cause problems with anything that expects the Map contract to be respected. And the infinite set of valid keys that containsKey() implies is likely to cause bad performance that's hard to diagnose. Not to say, though, that it might not serve some particular purpose.
  • maerics
    maerics almost 13 years
    +1 although sometimes its easier to reinvent the wheel than to introduce large dependencies for tiny slices of simple functionality.
  • Dave Newton
    Dave Newton almost 12 years
    It's not fine if you're using a NullObject pattern, though, or don't want to scatter null-checks throughout your code--a desire I completely understand.
  • bartosz.r
    bartosz.r over 11 years
    and funny thing is, that many projects that I work with already have something like this in classpath (either Apache Commons or Google Guava)
  • Krzysztof Jabłoński
    Krzysztof Jabłoński over 10 years
    You've got a point in not overriding the get method. On the other hand - your solution doesn't allow using the class via interface, which might often be the case.
  • ach
    ach almost 10 years
    getOrDefault is very nice, but requires the default definition every time the map is accessed. Defining a default value once would also have readability benefits when creating a static map of values.
  • Eyal
    Eyal over 9 years
    One problem with this approach is if the value is a complicated object. Map<String, List>#put won't work as expected.
  • Jack Satriano
    Jack Satriano over 9 years
    This is trivial to implement yourself. private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
  • dveim
    dveim over 8 years
    Does not work on ConcurrentHashMap. There, you should check get's result for null.
  • H.v.M.
    H.v.M. over 8 years
    @JackSatriano Yeah but you'd have to hard-code the default value, or have a static variable for it.
  • Dave Newton
    Dave Newton over 8 years
    @Eyal Depends on what you expect, I guess, but to make your point explicit, it would use the same list for each new key instead of creating a new list.
  • Dave Newton
    Dave Newton over 8 years
    @user2317480 More than that; you'd need to synchronize get.
  • hectorpal
    hectorpal about 7 years
    See below the answer using computeIfAbsent, better when the default value is expensive or should be different each time.
  • Spycho
    Spycho about 7 years
    Though it is worse for memory, and will only save computation time if the default value is expensive to construct / compute. If it's cheap, you'll probably find it performs worse, as it has to insert into the map, rather than just returning a default value. Certainly another option though.
  • numéro6
    numéro6 almost 7 years
    Not what OP asked: he wants no side effect on the map. Also, storing the default value for each absent key is a useless loss of memory space.
  • Vadzim
    Vadzim almost 7 years
    @numéro6, yes, this doesn't match exactly what OP asked but some googling people still find this side answer useful. As other answers mentioned, it's impossible to achieve exactly what OP asked without breaking map contract. Another workaround not mentioned here is to use another abstraction instead of Map.
  • numéro6
    numéro6 almost 7 years
    It's possible to achieve exactly what OP asked without breaking map contract. No workaround is needed, just using getOrDefault is the right (most updated) way, computeIfAbsent is the wrong way: you will lose time calling the mappingFunction and memory by storing the result (both for each missing keys). I can't see any good reason to do that instead of getOrDefault. What I'm describing is the exact reason why there are two distinct methods on the Map contract: there are two distinct use-cases that should not be confused (I had to fix some at work). This answer spread the confusion.
  • Admin
    Admin over 5 years
    This has an advantage over the accepted answer in that the default value is created by a factory. What if my default value is List<String> -- using the accepted answer I'd risk using the same list for each new key, rather than (say) a new ArrayList<String>() from a factory.
  • Pacerier
    Pacerier about 4 years
    @bartosz.r, definitely not on mobile
  • Pacerier
    Pacerier about 4 years
    where to store the static?
  • Joker
    Joker about 3 years
    Did anyone face this issue stackoverflow.com/questions/13032977/… after using this answer?