Is there a Java equivalent of Python's defaultdict?

后端 未结 8 1663
失恋的感觉
失恋的感觉 2021-02-05 00:05

In Python, the defaultdict class provides a convenient way to create a mapping from key -> [list of values], in the following example,



        
8条回答
  •  天命终不由人
    2021-02-05 00:35

    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 extends ForwardingMap {
      private final Map delegate;
      private final Supplier defaultSupplier;
    
      /**
       * Creates a map which uses the given value as the default for all
       * 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 create(V defaultValue) {
        return create(() -> defaultValue);
      }
    
      public static DefaultMap create(Supplier defaultSupplier) {
        return new DefaultMap<>(new HashMap<>(), defaultSupplier);
      }
    
      public DefaultMap(Map delegate, Supplier 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> defaultMap = DefaultMap.create(ArrayList::new);
    

提交回复
热议问题