2009-11-23 35 views
30

在Python中,defaultdict类提供了一个便捷的方式从key -> [list of values]创建一个映射,在下面的例子中,是否有Python的defaultdict的Java等价物?

from collections import defaultdict 
d = defaultdict(list) 
d[1].append(2) 
d[1].append(3) 
# d is now {1: [2, 3]} 

是否有一个相当于这个在Java中?

回答

20

没有任何东西给出默认代码的行为。然而,在Java中创建自己的默认字典并不会那么困难。

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

这个类可以使用方法如下:

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

此代码将打印:{1=[2, 3]}

+3

除了使用'Class',您还可以尝试传递一个番石榴'供应商' - 请参阅http://docs.guava-libraries.googlecode.com/git-history/v10.0/javadoc/com/google/common/base/Supplier.html – 2011-10-03 01:29:01

+0

或者,如果您不想要Guava依赖,只需在'DefaultDict'中定义你自己的'Supplier '接口。 – Soulman 2012-11-27 13:41:09

+0

我宁愿使用我自己的值构造'DefaultDict':public DefaultDict(V value){this.value = value; }' – 2014-06-07 01:43:22

5

您可以从Apache Commons使用MultiMap

除了阿帕奇集合
+1

链接:http://commons.apache.org/collections/api/org/apache/commons/collections/MultiMap.html – 2009-11-23 21:46:45

+0

链接中断 – HuStmpHrrr 2015-05-11 13:27:47

7

,也google collections检查:

类似的地图收藏,但它可以将多个值与一个键关联。如果使用相同的键但不同的值调用put(K,V)两次,则multimap包含键与两个值的映射。

2

只需使用Java运行时库,你可以使用一个HashMap并添加ArrayList握住你的值时,关键不存在或值添加到列表中时,关键是存在的。

1

从@ tendayi-mawushe的解决方案并没有为我与基本类型的工作(如InstantiationException Integer ),这里有一个与Integer,Double,Float一起使用的实现。我经常使用这些地图,并添加静态构造函数conveninence

import java.util.HashMap; 
import java.util.Map; 

/** Simulate the behaviour of Python's defaultdict */ 
public class DefaultHashMap<K, V> extends HashMap<K, V> { 
    private static final long serialVersionUID = 1L; 

    private final Class<V> cls; 
    private final Number defaultValue; 

    @SuppressWarnings({ "rawtypes", "unchecked" }) 
    public DefaultHashMap(Class factory) { 
     this.cls = factory; 
     this.defaultValue = null; 
    } 

    public DefaultHashMap(Number defaultValue) { 
     this.cls = null; 
     this.defaultValue = defaultValue; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public V get(Object key) { 
     V value = super.get(key); 
     if (value == null) { 
      if (defaultValue == null) { 
       try { 
        value = cls.newInstance(); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } else { 
       value = (V) defaultValue; 
      } 
      this.put((K) key, value); 
     } 
     return value; 
    } 

    public static <T> Map<T, Integer> intDefaultMap() { 
     return new DefaultHashMap<T, Integer>(0); 
    } 

    public static <T> Map<T, Double> doubleDefaultMap() { 
     return new DefaultHashMap<T, Double>(0d); 
    } 

    public static <T> Map<T, Float> floatDefaultMap() { 
     return new DefaultHashMap<T, Float>(0f); 
    } 

    public static <T> Map<T, String> stringDefaultMap() { 
     return new DefaultHashMap<T, String>(String.class); 
    } 
} 

和测试,为礼貌:

import static org.junit.Assert.assertEquals; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Map; 

import org.junit.Test; 

public class DefaultHashMapTest { 

    @Test 
    public void test() { 
     Map<String, List<String>> dm = new DefaultHashMap<String, List<String>>(
       ArrayList.class); 
     dm.get("nokey").add("one"); 
     dm.get("nokey").add("two"); 
     assertEquals(2, dm.get("nokey").size()); 
     assertEquals(0, dm.get("nokey2").size()); 
    } 

    @Test 
    public void testInt() { 
     Map<String, Integer> dm = DefaultHashMap.intDefaultMap(); 
     assertEquals(new Integer(0), dm.get("nokey")); 
     assertEquals(new Integer(0), dm.get("nokey2")); 
     dm.put("nokey", 3); 
     assertEquals(new Integer(0), dm.get("nokey2")); 
     dm.put("nokey3", 3); 
     assertEquals(new Integer(3), dm.get("nokey3")); 
    } 

    @Test 
    public void testString() { 
     Map<String, String> dm = DefaultHashMap.stringDefaultMap(); 
     assertEquals("", dm.get("nokey")); 
     dm.put("nokey1", "mykey"); 
     assertEquals("mykey", dm.get("nokey1")); 
    } 
} 
2

在最常见的情况下,你想有一个defaultdict,你会更快乐用适当设计的Multimap或Multiset,这是你真正想要的。 Multimap是一个关键 - >集合映射(默认为空集合),Multiset是关键 - > int映射(默认为零)。

Guava提供了非常好的实现​​这将涵盖几乎所有的用例。

但是(这就是为什么我发布了一个新答案)与Java 8现在可以复制defaultdict的剩余用例与任何现有Map

  • getOrDefault(),顾名思义,返回值,如果存在,或返回一个默认值。这存储在地图中的默认值。
  • computeIfAbsent()通过提供的函数计算出一个值(它总是可以返回相同的默认值)并且确实将存储在地图中,然后返回。

如果你想封装这些调用,您可以使用番石榴的ForwardingMap

public class DefaultMap<K, V> extends ForwardingMap<K, V> { 
    private final Map<K, V> delegate; 
    private final Supplier<V> default; 

    public static DefaultMap<K, V> create(V default) { 
    return create(() -> default); 
    } 

    public static DefaultMap<K, V> create(Supplier<V> default) { 
    return new DefaultMap<>(new HashMap<>(), default); 

    public DefaultMap<K, V>(Map<K, V> delegate, Supplier<V> default) { 
    this.delegate = delegate; 
    } 

    @Override 
    public V get(K key) { 
    return delegate().computeIfAbsent(key, k -> supplier.get()); 
    } 
} 

然后构造像这样的默认地图:

Map<String, List<String>> defaultMap = DefaultMap.create(ArrayList::new); 
+0

任何反馈,downvoter? – dimo414 2017-06-30 06:25:19

0

我写的库Guavaberry包含这样的数据结构:DefaultHashMap

它经过高度测试和记录。您可以通过Maven Central轻松找到并整合它。

主要的优点是它使用lambda来定义工厂方法。所以,你可以添加一个arbitrarly定义的类的实例(而不是依赖于默认的构造函数存在):

DefaultHashMap<Integer, List<String>> map = new DefaultHashMap(() -> new ArrayList<>()); 
map.get(11).add("first"); 

我希望能有所帮助。

+0

如果不是从'HashMap'扩展你使用'ForwardingMap'并允许调用者指定底层映射,它会更有用。喜欢构成继承。 – dimo414 2018-03-10 01:46:59

相关问题