Is there a Java equivalent of Python's defaultdict?
There is nothing that gives the behaviour of default dict out of the box. However creating your own default dict in Java would not be that difficult.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class DefaultDict<K, V> extends HashMap<K, V> {
Class<V> klass;
public DefaultDict(Class klass) {
this.klass = klass;
}
@Override
public V get(Object key) {
V returnValue = super.get(key);
if (returnValue == null) {
try {
returnValue = klass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
this.put((K) key, returnValue);
}
return returnValue;
}
}
This class could be used like below:
public static void main(String[] args) {
DefaultDict<Integer, List<Integer>> dict =
new DefaultDict<Integer, List<Integer>>(ArrayList.class);
dict.get(1).add(2);
dict.get(1).add(3);
System.out.println(dict);
}
This code would print: {1=[2, 3]}
In most common cases where you want a defaultdict
, you'll be even happier with a properly designed Multimap or Multiset, which is what you're really looking for. A Multimap is a key -> collection mapping (default is an empty collection) and a Multiset is a key -> int mapping (default is zero).
Guava provides very nice implementations of both Multimaps and Multisets which will cover almost all use cases.
But (and this is why I posted a new answer) with Java 8 you can now replicate the remaining use cases of defaultdict
with any existing Map
.
-
getOrDefault()
, as the name suggests, returns the value if present, or returns a default value. This does not store the default value in the map. -
computeIfAbsent()
computes a value from the provided function (which could always return the same default value) and does store the computed value in the map before returning.
If you want to encapsulate these calls you can use Guava's ForwardingMap
:
public class DefaultMap<K, V> extends ForwardingMap<K, V> {
private final Map<K, V> delegate;
private final Supplier<V> defaultSupplier;
/**
* Creates a map which uses the given value as the default for <i>all</i>
* keys. You should only use immutable values as a shared default key.
* Prefer {@link #create(Supplier)} to construct a new instance for each key.
*/
public static DefaultMap<K, V> create(V defaultValue) {
return create(() -> defaultValue);
}
public static DefaultMap<K, V> create(Supplier<V> defaultSupplier) {
return new DefaultMap<>(new HashMap<>(), defaultSupplier);
}
public DefaultMap<K, V>(Map<K, V> delegate, Supplier<V> defaultSupplier) {
this.delegate = Objects.requireNonNull(delegate);
this.defaultSupplier = Objects.requireNonNull(defaultSupplier);
}
@Override
public V get(K key) {
return delegate().computeIfAbsent(key, k -> defaultSupplier.get());
}
}
Then construct your default map like so:
Map<String, List<String>> defaultMap = DefaultMap.create(ArrayList::new);