Java Map implementation that returns a default value instead of null

前端 未结 6 876
萌比男神i
萌比男神i 2020-12-14 06:27

I have a Map> in my code, where I\'d avoid potential null pointers if the map\'s #get() method returned an empty l

相关标签:
6条回答
  • 2020-12-14 07:16

    Thanks to default methods, Java 8 now has this built in with Map::getOrDefault:

    Map<Integer, String> map = ...
    map.put(1, "1");
    System.out.println(map.getOrDefault(1, "2")); // "1"
    System.out.println(map.getOrDefault(2, "2")); // "2"
    
    0 讨论(0)
  • 2020-12-14 07:25

    Similar to previous posts except you can just override the get method if you want to change its behaviour.

    Map<String, List<String>> map = new LinkedHashMap<String, List<String>>() {
        public String get(Object key) {
            List<String> list = super.get(key);
            if (list == null && key instanceof String)
               super.put(key, list = new ArrayList<String>());
            return list;
        }
    }; 
    
    0 讨论(0)
  • 2020-12-14 07:27

    As noted in comments:

    Guava's computing map concept was superseded with LoadingCache. Also java 8 introduced to Map interface nice computeIfAbsent default method which doesn't break map contract and features lazy evaluation .


    Guava had the idea of a "computing map" which will execute a function to provide a value if it's not present. It was implemented in MapMaker.makeComputingMap; you could now use CacheBuilder - see CacheBuilder.build for more details.

    It may be overkill for what you're after - you might be better off just writing a Map implementation which has a Map (using composition rather than extending any particular implementation) and then just return the default if it's not present. Every method other than get could probably just delegate to the other map:

    public class DefaultingMap<K, V> implements Map<K, V>
    {
        private final Map<K, V> map;
        private final V defaultValue;
    
        public DefaultingMap(Map<K, V> map, V defaultValue)
        {
            this.map = map;
            this.defaultValue = defaultValue;
        }
    
        @Override public V get(Object key)
        {
            V ret = map.get(key);
            if (ret == null)
            {
                ret = defaultValue;
            }
            return ret;
        }
    
        @Override public int size()
        {
            return map.size();
        }
    
        // etc
    }
    
    0 讨论(0)
  • 2020-12-14 07:31

    Guava has a method to do exactly what you want. It is similar to Argote's answer.

    Map<String, List<String>> myMap = ...
    Functions.forMap(myMap, Arrays.asList("default", "value"));
    
    0 讨论(0)
  • 2020-12-14 07:32

    @Jon's answer is a good way to do what you are asking directly.

    But is strikes me that what you might be trying to implement is a "multimap"; i.e. a mapping from a key to a collection of values. If that is the case, then you should also look at the multimap classes in Guava or Apache commons collections.

    Look at:

    • the com.google.common.collect.Multimap interface and its implementations, or
    • the org.apache.commons.collections.MultiMap interface and its implementations.
    • the org.apache.commons.collections4.MultiMap interface and its implementations (a new version of the previous MultiMap; introduced in v4).
    0 讨论(0)
  • 2020-12-14 07:32

    It seems to me that you can simply write a wrapper function to get the value you want and if it's null return the default instead.

    Map<String, List<String>> myMap = new Map<String, List<String>>();
    
    public List<String> myGet(String key) {
        List<String> ret = myMap.get(key);
        if(ret == NULL) {
            return defaultList(); // where defaultList is a function that generates your default list.
        }
        return ret;
    }
    

    This assumes that your myMap is a global variable (for simplicity's sake), if that's not what you want then you can also extend Map or HashMap into something like MyMap and just include this extra function. That way you can use it from any place where a MyMap object is instantiated.

    0 讨论(0)
提交回复
热议问题