比較 - Pythonのdefaultdictに相当するJavaはありますか?
パイソン java 比較 (5)
Pythonでは、 defaultdict
クラスはkey -> [list of values]
からのマッピングを作成する便利な方法を提供します。次の例では、
from collections import defaultdict
d = defaultdict(list)
d[1].append(2)
d[1].append(3)
# d is now {1: [2, 3]}
Javaにこれに相当するものはありますか?
@ tendayi-mawusheの解決策は、Primitive型(例えばInstantiationException Integer
)で私にとってはうまくいかなかったが、ここではInteger、Double、Floatで動作する実装が1つある。 私はしばしば、これらを使ってMapsを使用し、便利さのために静的コンストラクタを追加しました
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"));
}
}
Apacheのコレクションに加えて、 Googleのコレクションもチェックしてください:
マップに似ていますが、複数の値を単一のキーに関連付けるコレクションです。 put(K、V)を同じキーで異なる値で2回呼び出すと、マルチマップにはキーから両方の値へのマッピングが含まれます。
Apache Commonsの MultiMap
を使用できます。
defaultdict
が必要な最も一般的なケースでは、あなたが本当に探しているものである、適切に設計されたMultimapまたはMultisetを使用するとさらに幸せになれます。 Multimapはキー - >コレクションマッピングです(デフォルトは空のコレクションです)。Multisetはキー - > intマッピングです(デフォルトはゼロ)。
Guavaは、ほぼすべてのユースケースをカバーするマルチマップとマルチセットの両方の非常に優れた実装を提供しています。
しかし、(これが私が新しい答えを投稿した理由です)Java 8では、 defaultdict
Map
残りのユースケースを既存のMap
で再現できるようになりました。
-
getOrDefault()
は、名前が示唆するように、値がある場合はそれを戻し、デフォルト値を戻します。 これは 、デフォルト値をマップに格納しません 。 -
computeIfAbsent()
は、指定された関数(常に同じデフォルト値を返す)から値を計算し、返される前に計算された値をマップに格納します。
これらの呼び出しをカプセル化する場合は、Guavaの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());
}
}
次に、デフォルトのマップを次のように作成します。
Map<String, List<String>> defaultMap = DefaultMap.create(ArrayList::new);
私はそのようなデータ構造を含むライブラリGuavaberryを書いた: DefaultHashMap 。
高度にテストされ、文書化されています。 Maven Centralを使用すると、簡単に見つけて統合することができます。
主なアドバンテージはラムダを使ってファクトリメソッドを定義することです。 したがって、(デフォルトのコンストラクタの存在に頼るのではなく)クラスの任意に定義されたインスタンスを追加することができます:
DefaultHashMap<Integer, List<String>> map = new DefaultHashMap(() -> new ArrayList<>());
map.get(11).add("first");
私はそれが助けになることを願っています。