3

In Python I can do the following:

d = { 'a': True, 'b': False }
print d.get('x', True)

It will print

True

In Java, the HashMap.get method does not have the second argument for specifying a default value. What is a correct, terse and readable way of achieving this? The following expression seems too redundant to me:

map.containsKey("x") ? map.get("x") : true
1
  • 1
    i think the way you posted it (after the edit) is pretty much the shortest way to write it in pure java Commented Nov 28, 2011 at 9:56

9 Answers 9

6

OP Edited his question, but I'll leave the answer just in case.

You should use

map.containsKey("x") 

instead of

map.get("x") != null

to test if map has a mapping for key.

Maps can contain null as valid value in mapping.

The same goes for python. It's not enough to test if dict[key] == None to know if a key is mapped to something.

Quick test:

d = {'a': None}

# returns True
d['a'] == None

# raises KeyError
d['b'] 

# returns 'Whatever'
d.get('b', 'Whatever')

So java equivalent to python dict.get() is

map.containsKey("x") ? map.get("x") : default_value

as OP said

Sign up to request clarification or add additional context in comments.

Comments

2

I reckon that...

Object o = map.get("x");
if(o == null)
    o = BOOLEAN.TRUE;

... is pretty much the most readable and efficient way of doing this. You're only doing one map lookup this way. Unfortunately, "Java" and "terse" don't tend to fit in the same sentence unless it's a negative.

2 Comments

it doesn't really do the same as python dict#get(key, default_value).
@soulcheck Good call. Forgot that the map might contain the value null for a key. Unfortunately Java doesn't have some convenient way of distinguishing the case of "no such key" versus "mapped to null", like Scala does. If null is a significant, possible value that shouldn't result in a default, then this approach is incorrect. You'd still need that call to containsKey like you showed.
1

Both are valid solutions (although introducing a local variable would probably be better...performance-wise) but it's not a good idea to do this everywhere in your code. Consider writing an adapter for HashMap that provides you with a new get() method that allows you to specify a default value. That's the best way to enhance the functionality of a class.

Comments

0

Both are wrong because they will do 2 lookups. Generally you'll want to do:

Boolean ret = map.get("x");
if (ret == null) {
    return true
}
return ret;

You can wrap this in a function

public static <K,V> V mapGet(Map<K, V> map, K key, V defaultValue) {
    V ret = map.get(key);
    if (ret == null) {
        return defaultValue;
    }
    return ret;
}

You can make it non-static if you want. If you are the person instantiating the map object (you don't get it from exernal code) you can subclass hashmap. If an API returns map instance then you can't use subclassing.

public class MyHashMap<K,V> extends HashMap<K,V> {
    public V get(K key, V default) {
        V ret = get(key);
        if (ret == null) {
            return defaultValue;
        }
        return ret;
    }
}

4 Comments

Having an extra lookup doesn't make it wrong, just slightly less efficient. (And it may be necessary if the map contains null values.)
Great function! I like this form of it (sorry for poor formatting): public static <K,V> V mapGet( Map<K, V> map, K key, V def ) { final V ret = map.get( key ); return ret == null ? def : ret; }
This is totally incorrect, as hashmaps can have null values. Please mark @soulcheck's answer as accepted: containsKey is the correct way to do it in Java, and yes, it's verbose :)
2 lookups is still constant time
0

map.get("x") == null ? true : map.get("x")

This sounds good but if you need it extensively you can implement your own implementation to handle this in get() itlself

Comments

0

I'd extend the map to expose a get(String key, Boolean defaultValue)... if not that I'd atleast put the terniary in a method, pushing the result of get.("x") onto the stack, instead of getting it twice... something like IfNull(map.get("x"), True)

If you need to provide default you probably have a "specialised" use for the map anyway, hence I guess the wrapper-class would be the way to go, not just for this behaviour, but for any other (current AND FUTURE) specialised behaviours/responsibilities.

That's just how I'd tackle it... I'm NO object-oriented design guru though ;-)

Cheers. Keith.

Comments

0

A short and reasonably generic way to do it, although not necessarily good practice, would be a helper class and a static import:

Helper.java

package vlad;
import java.util.Map;
public class Helper{    
    public static <K,V> V get(Map<K,V> m, K key, V def){
        V v = m.get(key);
        return (v!=null) ? v : def;
    }
}

Test.java

import java.util.*;
import static vlad.Helper.get;

class Test{

    public static void main(String[]args){

        Map<String, Integer> m = new HashMap<String,Integer>();

        m.put("forty-two", 42);
        m.put("three", 3);
        m.put("one", 1);

        System.out.println(get(m,"forty-two",-1));
        System.out.println(get(m,"three",-1));
        System.out.println(get(m,"one",-1));
        System.out.println(get(m,"something_else",-1));

    }
}

Comments

0

In Java 8, there's a getOrDefault method on Map, which should act like Python's dicts' get method: https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#getOrDefault-java.lang.Object-V-.

Comments

0
    HashMap<Character, String> d = new HashMap<Character, String>();  
    System.out.println(d.getOrDefault('c', "pizza")); 

1 Comment

This answer is ok, but a comment would make it better for beginners.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.