Hashmap to Return Default Value for Non-Found Keys

HashMap to return default value for non-found keys?

[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;
}
}

Java Map implementation that returns a default value instead of null

@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).

Initializing a HashMap with default values?

There is no way to initialize a map with a default value in Java, and your second version will not create a map with a default value of infinity, but instead will try to create an infinitely large map. (Not really, but it'll try creating the largest map possible.)

Instead, modify the algorithm: anytime you do map.get(key), check if the value is null and, if so, replace it with infinity.

HashMap to return default value for non-found keys?

[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;
}
}

Setting Default Values in HashMap

Better check the return value instead of changing the way a Map works IMO. The Commons Lang StringUtils.defaultString(String) method should do the trick:

Map<String, String> test = new HashMap<>();
assertEquals("", StringUtils.defaultString(test.get("hello")));
assertEquals("DEFAULT", StringUtils.defaultString(test.get("hello"), "DEFAULT"));

StringUtils JavaDoc is here.

Extend HashMap to return empty HashSet for non-found keys

First it should be noted that in Java-8 you can use instead:

isbnToId.computeIfAbsent(isbn, k -> new HashSet<>()).add(_id);

Second, if you really want to do something like this in previous Java versions, you'd better to create separate method for this purpose (for example, getOrDefault()) in order not to violate the contract. Third, you need to create new HashSet<>() for every new key. If you return the same instance, it will be shared between given keys. If you don't expect users to modify it, it's better to use unmodifiable Collections.emptySet() as default value. This way users may safely do isbnToId.getOrDefault(isbn).contains(_id), but trying isbnToId.getOrDefault(isbn).add(_id) will result in exception. If you want to support the modification (prior to Java-8), you can, for example, pass the element class to the constructor instead:

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;
}
}

Usage example:

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]}

Here we assume that instantiating the passed class with default constructor is ok. An alternative would be to pass the factory interface which is capable to produce the default values.

Java Map - log message when key is not found in getOrDefault

Map<String, List<String>> map = new HashMap<>();
List<String> l = new ArrayList<>();
l.add("b");
map.put("a", l);

Yes, you can do it in a single statement. Use .compute().

map.compute("a", (k, v) -> {
if (v == null) {
System.out.println("Key Not Found");
return new ArrayList<>();
}
return v;
}).forEach(System.out::println);

There's also computeIfAbsent() which will only compute the lambda if the key is not present.


Note, from the documentation:

Attempts to compute a mapping for the specified key and its current
mapped value (or null if there is no current mapping).

This will add the key which was not found in your map.

If you want to remove those keys later, then simply add those keys to a list inside the if and remove them in one statement like this:

map.keySet().removeAll(listToRemove);

HashMap - getting First Key value

You can try this:

 Map<String,String> map = new HashMap<>();
Map.Entry<String,String> entry = map.entrySet().iterator().next();
String key = entry.getKey();
String value = entry.getValue();

Keep in mind, HashMap does not guarantee the insertion order. Use a LinkedHashMap to keep the order intact.

Eg:

 Map<String,String> map = new LinkedHashMap<>();
map.put("Active","33");
map.put("Renewals Completed","3");
map.put("Application","15");
Map.Entry<String,String> entry = map.entrySet().iterator().next();
String key= entry.getKey();
String value=entry.getValue();
System.out.println(key);
System.out.println(value);

Output:

 Active
33

Update:
Getting first key in Java 8 or higher versions.

Optional<String> firstKey = map.keySet().stream().findFirst();
if (firstKey.isPresent()) {
String key = firstKey.get();
}

How does one create a HashMap with a default value in Rust?

Answering the problem you have...

I am looking to maintain a counter for a set of keys.

Then you want to look at How to lookup from and insert into a HashMap efficiently?. Hint: *map.entry(key).or_insert(0) += 1


Answering the question you asked...

How does one create a HashMap with a default value in Rust?

No, HashMaps do not have a place to store a default. Doing so would cause every user of that data structure to allocate space to store it, which would be a waste. You'd also have to handle the case where there is no appropriate default, or when a default cannot be easily created.

Instead, you can look up a value using HashMap::get and provide a default if it's missing using Option::unwrap_or:

use std::collections::HashMap;

fn main() {
let mut map: HashMap<char, usize> = HashMap::new();
map.insert('a', 42);

let a = map.get(&'a').cloned().unwrap_or(0);
let b = map.get(&'b').cloned().unwrap_or(0);

println!("{}, {}", a, b); // 42, 0
}

If unwrap_or doesn't work for your case, there are several similar functions that might:

  • Option::unwrap_or_else
  • Option::map_or
  • Option::map_or_else

Of course, you are welcome to wrap this in a function or a data structure to provide a nicer API.


ArtemGr brings up an interesting point:

in C++ there's a notion of a map inserting a default value when a key is accessed. That always seemed a bit leaky though: what if the type doesn't have a default? Rust is less demanding on the mapped types and more explicit about the presence (or absence) of a key.

Rust adds an additional wrinkle to this. Actually inserting a value would require that simply getting a value can also change the HashMap. This would invalidate any existing references to values in the HashMap, as a reallocation might be required. Thus you'd no longer be able to get references to two values at the same time! That would be very restrictive.



Related Topics



Leave a reply



Submit