2009-11-23 73 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

相關問題