Javaで静的Map
をどのように初期化しますか?
方法1:静的イニシャライザ
方法2:インスタンス初期化子(無名サブクラス)またはその他の方法
それぞれの長所と短所は何ですか?
これが2つの方法を説明する例です。
import Java.util.HashMap;
import Java.util.Map;
public class Test {
private static final Map<Integer, String> myMap = new HashMap<Integer, String>();
static {
myMap.put(1, "one");
myMap.put(2, "two");
}
private static final Map<Integer, String> myMap2 = new HashMap<Integer, String>(){
{
put(1, "one");
put(2, "two");
}
};
}
インスタンス初期化子は、この場合は単なる構文上の糖です。初期化のためだけに追加の無名クラスが必要な理由はわかりません。そして、作成されているクラスが最終的であるなら、それは機能しません。
スタティックイニシャライザを使って不変マップを作成することもできます。
public class Test {
private static final Map<Integer, String> myMap;
static {
Map<Integer, String> aMap = ....;
aMap.put(1, "one");
aMap.put(2, "two");
myMap = Collections.unmodifiableMap(aMap);
}
}
Guava 静的で不変のマップを初期化する方法が好きです:
static final Map<Integer, String> MY_MAP = ImmutableMap.of(
1, "one",
2, "two"
);
ご覧のとおり、非常に簡潔です( ImmutableMap
の便利なファクトリメソッドのため)。
マップに5つ以上のエントリを含める場合は、ImmutableMap.of()
を使用できなくなります。代わりに、次の行に沿って ImmutableMap.builder()
を試してください。
static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
.put(1, "one")
.put(2, "two")
// ...
.put(15, "fifteen")
.build();
Guavaの不変コレクションユーティリティの利点の詳細については、 Imutable Collections Explainedin Guava User Guide を参照してください。
(のサブセット)GuavaはGoogle Collectionsと呼ばれていました。 Javaプロジェクトでこのライブラリをまだ使用していない場合、私は強く試してみることをお勧めします! fellow SOユーザーが同意する のように、グアバはすぐに最も人気のあるJava用の無料のサードパーティライブラリの1つになりました。 (あなたがそれに慣れていない場合、そのリンクの背後にあるいくつかの優れた学習リソースがあります。)
Update(2015):Java 8については、まあ、グアバアプローチを使用するのは、他のどの方法よりもずっとクリーンだからです。 Guava依存関係が必要ない場合は、 プレーンな古いinitメソッド を検討してください。 2次元配列とStream API のハックは、私に尋ねるとかなりく、キーと値が同じタイプではないMapを作成する必要がある場合はquestionくなります(質問のMap<Integer, String>
など) )。
一般的なグアバの将来については、Java 8に関して、Louis Wasserman said this 2014年、および[update ] 2016年に Guava 21はJava 8 を必要とし、適切にサポートすることが発表されました。
更新(2016):As Tagir Valeevが指摘 、Java 9コレクションに コンビニエンスファクトリメソッド を追加することにより、純粋なJDKのみを使用して、これを最終的にクリーンにします:
static final Map<Integer, String> MY_MAP = Map.of(
1, "one",
2, "two"
);
私は使うだろう:
public class Test {
private static final Map<Integer, String> MY_MAP = createMap();
private static Map<Integer, String> createMap() {
Map<Integer, String> result = new HashMap<Integer, String>();
result.put(1, "one");
result.put(2, "two");
return Collections.unmodifiableMap(result);
}
}
Java 5では、このよりコンパクトな構文が提供されています。
static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
put("Up", "Down");
put("Charm", "Strange");
put("Top", "Bottom");
}};
2番目の方法の利点の1つは、後でコレクションを更新するものがないことを保証するためにCollections.unmodifiableMap()
でラップできることです。
private static final Map<Integer, String> CONSTANT_MAP =
Collections.unmodifiableMap(new HashMap<Integer, String>() {{
put(1, "one");
put(2, "two");
}});
// later on...
CONSTANT_MAP.put(3, "three"); // going to throw an exception!
これは、Java 8の1行スタティックマップイニシャライザです。
private static final Map<String, String> EXTENSION_TO_MIMETYPE =
Arrays.stream(new String[][] {
{ "txt", "text/plain" },
{ "html", "text/html" },
{ "js", "application/javascript" },
{ "css", "text/css" },
{ "xml", "application/xml" },
{ "png", "image/png" },
{ "gif", "image/gif" },
{ "jpg", "image/jpeg" },
{ "jpeg", "image/jpeg" },
{ "svg", "image/svg+xml" },
}).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));
編集:質問のようにMap<Integer, String>
を初期化するには、次のようなものが必要です。
static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
{1, "one"},
{2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));
編集(2):new SimpleEntry<>(k, v)
呼び出しのストリームを使用するi_am_zeroによるより良い、混合型対応バージョンがあります。その答えをチェックしてください: https://stackoverflow.com/a/37384773/3950982
Eclipse Collections を使用すると、以下のすべてが機能します。
import Java.util.Map;
import org.Eclipse.collections.api.map.ImmutableMap;
import org.Eclipse.collections.api.map.MutableMap;
import org.Eclipse.collections.impl.factory.Maps;
public class StaticMapsTest
{
private static final Map<Integer, String> MAP =
Maps.mutable.with(1, "one", 2, "two");
private static final MutableMap<Integer, String> MUTABLE_MAP =
Maps.mutable.with(1, "one", 2, "two");
private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();
private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
Maps.mutable.with(1, "one", 2, "two").asSynchronized();
private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
Maps.mutable.with(1, "one", 2, "two").toImmutable();
private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
Maps.immutable.with(1, "one", 2, "two");
}
Eclipseコレクションを使ってプリミティブマップを静的に初期化することもできます。
import org.Eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.Eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.Eclipse.collections.impl.factory.primitive.IntObjectMaps;
public class StaticPrimitiveMapsTest
{
private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two");
private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two")
.asUnmodifiable();
private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two")
.asSynchronized();
private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two")
.toImmutable();
private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
IntObjectMaps.immutable.<String>empty()
.newWithKeyValue(1, "one")
.newWithKeyValue(2, "two");
}
注: 私はEclipseコレクションのコミッターです
Map.ofEntries
を次のように使用できます。
import static Java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
entry(1, "one"),
entry(2, "two"),
entry(3, "three"),
entry(4, "four"),
entry(5, "five"),
entry(6, "six"),
entry(7, "seven"),
entry(8, "eight"),
entry(9, "nine"),
entry(10, "ten"));
Tagirの回答で提案されているようにMap.of
を使用することもできます here しかし、Map.of
を使用して10以上のエントリを持つことはできません。
マップエントリのストリームを作成できます。 Java.util.AbstractMap
には、 SimpleEntry と SimpleImmutableEntry の2つのEntry
の実装が既にあります。この例では、前者を次のように利用できます。
import Java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
new SimpleEntry<>(1, "one"),
new SimpleEntry<>(2, "two"),
new SimpleEntry<>(3, "three"),
new SimpleEntry<>(4, "four"),
new SimpleEntry<>(5, "five"),
new SimpleEntry<>(6, "six"),
new SimpleEntry<>(7, "seven"),
new SimpleEntry<>(8, "eight"),
new SimpleEntry<>(9, "nine"),
new SimpleEntry<>(10, "ten"))
.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));
私はこの状況では無名サブクラスを作成することは決してないでしょう。マップを変更不可能にしたい場合は、静的初期化子も同様に機能します。
private static final Map<Integer, String> MY_MAP;
static
{
Map<Integer, String>tempMap = new HashMap<Integer, String>();
tempMap.put(1, "one");
tempMap.put(2, "two");
MY_MAP = Collections.unmodifiableMap(tempMap);
}
多分それは グーグルコレクション をチェックアウトするのは面白いです。彼らが彼らのページに持っているビデオ。それらはマップとセットを初期化するさまざまな方法を提供し、不変コレクションも提供します。
更新:このライブラリの名前は Guava になりました。
私は匿名クラスが好きです。
public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
{
put(1, "some value");
//rest of code here
}
});
public class Test {
private static final Map<Integer, String> myMap;
static {
Map<Integer, String> aMap = ....;
aMap.put(1, "one");
aMap.put(2, "two");
myMap = Collections.unmodifiableMap(aMap);
}
}
複数の定数を宣言した場合、そのコードは静的ブロックで記述されることになり、今後のメンテナンスは困難です。それで、無名クラスを使うほうが良いです。
public class Test {
public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
{
put(1, "one");
put(2, "two");
}
});
}
そして定数として扱うことができない他の点では定数にunmodifiableMapを使用することをお勧めします。
静的ブロックスタイルよりも「二重括弧の初期化」スタイルを強くお勧めできます。
匿名クラス、オーバーヘッド、パフォーマンスなどが好きではないと誰かがコメントするかもしれません。
しかし、私がもっと考えるのはコードの読みやすさと保守性です。この観点では、私は二重括弧が静的な方法よりも優れたコードスタイルであると立てます。
さらに、匿名クラスのGCを知っていれば、new HashMap(Map map)
を使用していつでも通常のHashMapに変換できます。
別の問題に直面するまでこれを行うことができます。もしそうなら、あなたはそれのために完全な別のコーディングスタイル(例えば、静的でない、ファクトリークラス)を使うべきです。
いつものようにApache-commonsは適切なメソッドを持っています MapUtils.putAll(Map、Object []) :
たとえば、カラーマップを作成するには
Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
{"RED", "#FF0000"},
{"GREEN", "#00FF00"},
{"BLUE", "#0000FF"}
});
GuavaのImmutableMap.of()
を使用したくない(または使用できない)場合、または可変のMap
が必要な場合は、ここが私のお気に入りです。
public static <A> Map<String, A> asMap(Object... keysAndValues) {
return new LinkedHashMap<String, A>() {{
for (int i = 0; i < keysAndValues.length - 1; i++) {
put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
}
}};
}
それは非常にコンパクトです、そしてそれは漂遊値(すなわち値のない最後のキー)を無視します。
使用法:
Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));
変更不可能なマップが必要な場合は、ついにJava 9がクールなファクトリメソッドof
をMap
インターフェースに追加しました。同様のメソッドがSet、Listにも追加されています。
Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");
私は、静的なイニシャライザを使って匿名クラスを生成しないようにします(これ以上の目的はありません)ので、静的なイニシャライザを使って初期化するヒントを示します。記載されているすべての解決策/ヒントはタイプセーフです。
注: 地図上の変更を不可能にすることについては何も言っていないので、ここでは省略しますが、Collections.unmodifiableMap(map)
を使えば簡単に実行できることを知っています。
最初のヒント
1つ目のヒントは、マップへのローカル参照を作成し、それにSHORTという名前を付けることです。
private static final Map<Integer, String> myMap = new HashMap<>();
static {
final Map<Integer, String> m = myMap; // Use short name!
m.put(1, "one"); // Here referencing the local variable which is also faster!
m.put(2, "two");
m.put(3, "three");
}
セカンドチップ
2番目のヒントは、エントリを追加するためのヘルパーメソッドを作成できるということです。また、このヘルパーメソッドをpublicにすることもできます。
private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
p(1, "one"); // Calling the helper method.
p(2, "two");
p(3, "three");
}
private static void p(Integer k, String v) {
myMap2.put(k, v);
}
ここのヘルパーメソッドはmyMap2
に要素を追加することしかできないので再利用できません。再利用可能にするために、マップ自体をヘルパーメソッドのパラメータにすることができますが、初期化コードはそれ以上短くはなりません。
第三のヒント
3つ目のヒントは、移植機能を備えた再利用可能なビルダーのようなヘルパークラスを作成できることです。これは本当に単純な10行ヘルパークラスで、型保証されています。
public class Test {
private static final Map<Integer, String> myMap3 = new HashMap<>();
static {
new B<>(myMap3) // Instantiating the helper class with our map
.p(1, "one")
.p(2, "two")
.p(3, "three");
}
}
class B<K, V> {
private final Map<K, V> m;
public B(Map<K, V> m) {
this.m = m;
}
public B<K, V> p(K k, V v) {
m.put(k, v);
return this; // Return this for chaining
}
}
あなたが作成している匿名クラスはうまくいきます。ただし、これは inner クラスであることに注意してください。そのため、周囲のクラスインスタンスへの参照が含まれます。ですから、あなたはそれを使って特定のことをすることができないことに気づくでしょう( XStream を使います)。非常に奇妙なエラーが出ます。
そうは言っても、あなたが知っている限りこのアプローチは大丈夫です。私は簡潔な方法であらゆる種類のコレクションを初期化するためにそれをほとんどの場合使用します。
編集:これは静的クラスであることをコメントで正しく指摘した。明らかに私はこれを十分に詳しく読みませんでした。しかし、私のコメント do はまだ匿名の内部クラスに適用されます。
もっと簡潔で比較的安全なものが必要な場合は、コンパイル時の型チェックを実行時に変更するだけです。
static final Map<String, Integer> map = MapUtils.unmodifiableMap(
String.class, Integer.class,
"cat", 4,
"dog", 2,
"frog", 17
);
この実装はエラーをキャッチする必要があります。
import Java.util.HashMap;
public abstract class MapUtils
{
private MapUtils() { }
public static <K, V> HashMap<K, V> unmodifiableMap(
Class<? extends K> keyClazz,
Class<? extends V> valClazz,
Object...keyValues)
{
return Collections.<K, V>unmodifiableMap(makeMap(
keyClazz,
valClazz,
keyValues));
}
public static <K, V> HashMap<K, V> makeMap(
Class<? extends K> keyClazz,
Class<? extends V> valClazz,
Object...keyValues)
{
if (keyValues.length % 2 != 0)
{
throw new IllegalArgumentException(
"'keyValues' was formatted incorrectly! "
+ "(Expected an even length, but found '" + keyValues.length + "')");
}
HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);
for (int i = 0; i < keyValues.length;)
{
K key = cast(keyClazz, keyValues[i], i);
++i;
V val = cast(valClazz, keyValues[i], i);
++i;
result.put(key, val);
}
return result;
}
private static <T> T cast(Class<? extends T> clazz, Object object, int i)
{
try
{
return clazz.cast(object);
}
catch (ClassCastException e)
{
String objectName = (i % 2 == 0) ? "Key" : "Value";
String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
}
}
}
Java 8では、次のパターンを使うようになりました。
private static final Map<String, Integer> MAP = Stream.of(
new AbstractMap.SimpleImmutableEntry<>("key1", 1),
new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
それは最も簡潔で少し回り道ではありませんが、
Java.util
以外には何も必要ありません。あなたの2番目のアプローチ (二重括弧の初期化) は 反パターン と考えられているので、私は最初のアプローチに行きます。
静的Mapを初期化するもう1つの簡単な方法は、このユーティリティ関数を使用することです。
public static <K, V> Map<K, V> mapOf(Object... keyValues) {
Map<K, V> map = new HashMap<>(keyValues.length / 2);
for (int index = 0; index < keyValues.length / 2; index++) {
map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
}
return map;
}
Map<Integer, String> map1 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");
注:Java 9
では、 Map.of を使用できます。
Javaはマップリテラルをサポートしていないため、マップインスタンスは常に明示的にインスタンス化され、生成される必要があります。
幸いなことに、 ファクトリメソッド を使ってJavaのマップリテラルの振る舞いを近似することが可能です。
例えば、次のとおりです。
public class LiteralMapFactory {
// Creates a map from a list of entries
@SafeVarargs
public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
LinkedHashMap<K, V> map = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : entries) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
// Creates a map entry
public static <K, V> Map.Entry<K, V> entry(K key, V value) {
return new AbstractMap.SimpleEntry<>(key, value);
}
public static void main(String[] args) {
System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
}
}
出力:
{a = 1、b = 2、c = 3}
一度に1つの要素でマップを作成して設定するよりもはるかに便利です。
私が使っている(そして好きになった)アプローチがどんな答えにも掲載されているのを見たことがないので、ここでは:
静的イニシャライザを使うのは嫌いなので、匿名クラスは好きではありません。インスタンスごとに新しいクラスを作成するからです。
代わりに、私はこのように見える初期化を好みます:
map(
entry("keyA", "val1"),
entry("keyB", "val2"),
entry("keyC", "val3")
);
残念ながら、これらのメソッドは標準のJavaライブラリの一部ではないので、次のメソッドを定義するユーティリティライブラリを作成(または使用)する必要があります。
public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
public static <K,V> Map.Entry<K,V> entry(K key, V val)
(メソッド名の前に付く必要がないようにするために 'import static'を使うことができます)
他のコレクション(list、set、sortedSet、sortedMapなど)にも同様の静的メソッドを提供すると便利です。
これは、jsonオブジェクトの初期化ほど素敵ではありませんが、読みやすさに関する限り、その方向への一歩です。
私は静的初期化子の構文が嫌いで、匿名のサブクラスに納得できません。一般に、静的初期化子を使用することのすべての短所と、前の回答で述べた無名サブクラスを使用することのすべての短所に同意します。一方、これらの記事に掲載されている長所は私にとって十分ではありません。私は静的な初期化方法を使うのが好きです。
public class MyClass {
private static final Map<Integer, String> myMap = prepareMap();
private static Map<Integer, String> prepareMap() {
Map<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "one");
hashMap.put(2, "two");
return hashMap;
}
}
マップに値を1つだけ追加する必要がある場合は、 Collections.singletonMap を使用できます。
Map<K, V> map = Collections.singletonMap(key, value)
私は答えを読みました、そして私は私自身のマップビルダーを書くことにしました。コピー&ペーストして楽しんでください。
import Java.util.Collections;
import Java.util.HashMap;
import Java.util.Map;
/**
* A tool for easy creation of a map. Code example:<br/>
* {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
* @param <K> key type (inferred by constructor)
* @param <V> value type (inferred by constructor)
* @author Vlasec (for http://stackoverflow.com/a/30345279/1977151)
*/
public class MapBuilder <K, V> {
private Map<K, V> map = new HashMap<>();
/** Constructor that also enters the first entry. */
private MapBuilder(K key, V value) {
and(key, value);
}
/** Factory method that creates the builder and enters the first entry. */
public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
return new MapBuilder<>(key, value);
}
/** Puts the key-value pair to the map and returns itself for method chaining */
public MapBuilder<K, V> and(K key, V value) {
map.put(key, value);
return this;
}
/**
* If no reference to builder is kept and both the key and value types are immutable,
* the resulting map is immutable.
* @return contents of MapBuilder as an unmodifiable map.
*/
public Map<K, V> build() {
return Collections.unmodifiableMap(map);
}
}
編集:最近、私は公的な静的メソッドof
をかなり頻繁に見つけています、そして私はそれが好きです。それをコードに追加してコンストラクタを非公開にし、静的ファクトリメソッドパターンに切り替えました。
編集2:さらに最近では、静的インポートを使用する場合はかなり悪いように見えるので、私はもはやof
と呼ばれる静的メソッドが好きではありません。代わりにmapOf
に名前を変更し、静的インポートにより適したものにしました。
JEP 269 はCollections API用の便利なファクトリメソッドを提供します。このファクトリメソッドは現在のJavaバージョン(8)にはありませんが、Java 9リリースで計画されています。
Map
には2つのファクトリメソッドがあります:of
とofEntries
。 of
を使うと、キーと値のペアを交互に渡すことができます。例えば、{age: 27, major: cs}
のようなMap
を作成するためには、
Map<String, Object> info = Map.of("age", 27, "major", "cs");
現在of
には10個のオーバーロードされたバージョンがあるので、10個のキーと値のペアを含むマップを作成できます。この制限やキー/値の変更が気に入らない場合は、ofEntries
を使用できます。
Map<String, Object> info = Map.ofEntries(
Map.entry("age", 27),
Map.entry("major", "cs")
);
of
とofEntries
はどちらも不変のMap
を返すので、構築後にそれらの要素を変更することはできません。 JDK 9 Early Access を使ってこれらの機能を試すことができます。
ええと…私は列挙型が好きです;)
enum MyEnum {
ONE (1, "one"),
TWO (2, "two"),
THREE (3, "three");
int value;
String name;
MyEnum(int value, String name) {
this.value = value;
this.name = name;
}
static final Map<Integer, String> MAP = Stream.of( values() )
.collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}
これは、Apache commons-langを使用します。ほとんどの場合、クラスパス上に既にあります。
Map<String, String> collect = Stream.of(
Pair.of("hello", "world"),
Pair.of("abc", "123"),
Pair.of("Java", "eight")
).collect(Collectors.toMap(Pair::getKey, Pair::getValue));
Java 8では、手続き型アプローチをSupplier
でラップすることもできます。
Map<String,String> m = ((Supplier<Map<String,String>>)(() -> {
Map<String,String> result = new HashMap<>();
result.put("foo","hoo");
...
return result;
)).get();
これは仮想的な方法にすぎませんが、実際にワンライナーが必要な場合に便利です。
GuavaのNice ImmutableMapクラスを使用しても、可変マップを流暢に作成したいことがあります。 Java 8が登場したとき、静的ブロックと匿名のサブタイプの事を避けたいと思った私は、 Fluent と呼ばれる小さなライブラリを書きました。
// simple usage, assuming someMap is a Map<String, String> already declared
Map<String, String> example = new Fluent.HashMap<String, String>()
.append("key1", "val1")
.append("key2", "val2")
.appendAll(someMap);
Java 8インターフェースのデフォルト設定では、私は面倒な繰り返しなしに、すべての標準Java Map実装(つまりHashMap、ConcurrentSkipListMapなど)に対してFluent.Mapメソッドを実装できました。
変更不可能なマップもまた単純です。
Map<String, Integer> immutable = new Fluent.LinkedHashMap<String, Integer>()
.append("one", 1)
.append("two", 2)
.append("three", 3)
.unmodifiable();
ソース、ドキュメント、例については https://github.com/alexheretic/fluent を参照してください。
Java 8が出た今、この質問は再訪を正当化する。私はそれを試してみました - たぶんあなたはこのように見えるかなり素敵で簡潔な(しかしタイプセーフな)マップリテラル構文を得るためにラムダ式構文を利用することができるように見えます:
Map<String,Object> myMap = hashMap(
bob -> 5,
TheGimp -> 8,
incredibleKoolAid -> "James Taylor",
heyArnold -> new Date()
);
Map<String,Integer> typesafeMap = treeMap(
a -> 5,
bee -> 8,
sea -> 13
deep -> 21
);
テストされていないサンプルコードは https://Gist.github.com/galdosd/10823529 これに関する他の人の意見に興味があるでしょう(それは少し悪質です...)
データの文字列表現を使用できる場合、これはJava 8でもオプションです。
static Map<Integer, String> MAP = Stream.of(
"1=one",
"2=two"
).collect(Collectors.toMap(k -> Integer.parseInt(k.split("=")[0]), v -> v.split("=")[1]));
初期化コンストラクタを定義しているがデフォルトコンストラクタは定義していない抽象クラスを具体的に実現しているが、自分のサブクラスにデフォルトコンストラクタを持たせたい場合は、静的イニシャライザ "技法"を使用するのが好きです。
例えば:
public abstract class Shape {
public static final String COLOR_KEY = "color_key";
public static final String OPAQUE_KEY = "opaque_key";
private final String color;
private final Boolean opaque;
/**
* Initializing constructor - note no default constructor.
*
* @param properties a collection of Shape properties
*/
public Shape(Map<String, Object> properties) {
color = ((String) properties.getOrDefault(COLOR_KEY, "black"));
opaque = (Boolean) properties.getOrDefault(OPAQUE_KEY, false);
}
/**
* Color property accessor method.
*
* @return the color of this Shape
*/
public String getColor() {
return color;
}
/**
* Opaque property accessor method.
*
* @return true if this Shape is opaque, false otherwise
*/
public Boolean isOpaque() {
return opaque;
}
}
そしてこのクラスの私の具体的な実現 - しかしそれはデフォルトコンストラクタを欲している/必要としています:
public class SquareShapeImpl extends Shape {
private static final Map<String, Object> DEFAULT_PROPS = new HashMap<>();
static {
DEFAULT_PROPS.put(Shape.COLOR_KEY, "yellow");
DEFAULT_PROPS.put(Shape.OPAQUE_KEY, false);
}
/**
* Default constructor -- intializes this square to be a translucent yellow
*/
public SquareShapeImpl() {
// the static initializer was useful here because the call to
// this(...) must be the first statement in this constructor
// i.e., we can't be mucking around and creating a map here
this(DEFAULT_PROPS);
}
/**
* Initializing constructor -- create a Square with the given
* collection of properties.
*
* @param props a collection of properties for this SquareShapeImpl
*/
public SquareShapeImpl(Map<String, Object> props) {
super(props);
}
}
それからこのデフォルトのコンストラクタを使うために、我々は単にする:
public class StaticInitDemo {
public static void main(String[] args) {
// create a translucent, yellow square...
Shape defaultSquare = new SquareShapeImpl();
// etc...
}
}
匿名クラスの構文が好きです。それはほんの少しコードです。しかし、私が見つけた1つの主要な詐欺はあなたがリモート処理によってそのオブジェクトを直列化することができないことです。あなたはリモート側で匿名クラスを見つけることができないことについての例外を得るでしょう。
これは AbacusUtil によるコードです。
Map<Integer, String> map = N.asMap(1, "one", 2, "two");
// Or for Immutable map
ImmutableMap<Integer, String> = ImmutableMap.of(1, "one", 2, "two");
宣言:私はAbacusUtilの開発者です。
注:この答えは、実際には質問に属します HashMapを(文字通りの方法で)直接初期化する方法? しかし、この質問の[複製]としてマークされているため...
Java 9の Map.of() (これも10個のマッピングに制限されています)の前に、選択したMap
実装を拡張できます。
public class InitHashMap<K, V> extends HashMap<K, V>
HashMap
のコンストラクタを再実装します。
public InitHashMap() {
super();
}
public InitHashMap( int initialCapacity, float loadFactor ) {
super( initialCapacity, loadFactor );
}
public InitHashMap( int initialCapacity ) {
super( initialCapacity );
}
public InitHashMap( Map<? extends K, ? extends V> m ) {
super( m );
}
Aerthelの答え に触発されているが、Object...
および<K, V>
タイプを使用することで汎用的なコンストラクターを追加します。
public InitHashMap( final Object... keyValuePairs ) {
if ( keyValuePairs.length % 2 != 0 )
throw new IllegalArgumentException( "Uneven number of arguments." );
K key = null;
int i = -1;
for ( final Object keyOrValue : keyValuePairs )
switch ( ++i % 2 ) {
case 0: // key
if ( keyOrValue == null )
throw new IllegalArgumentException( "Key[" + (i >> 1) + "] is <null>." );
key = (K) keyOrValue;
continue;
case 1: // value
put( key, (V) keyOrValue );
}
}
public static void main( final String[] args ) {
final Map<Integer, String> map = new InitHashMap<>( 1, "First", 2, "Second", 3, "Third" );
System.out.println( map );
}
{1=First, 2=Second, 3=Third}
Map
インターフェイスも同様に拡張できます。
public interface InitMap<K, V> extends Map<K, V> {
static <K, V> Map<K, V> of( final Object... keyValuePairs ) {
if ( keyValuePairs.length % 2 != 0 )
throw new IllegalArgumentException( "Uneven number of arguments." );
final Map<K, V> map = new HashMap<>( keyValuePairs.length >> 1, .75f );
K key = null;
int i = -1;
for ( final Object keyOrValue : keyValuePairs )
switch ( ++i % 2 ) {
case 0: // key
if ( keyOrValue == null )
throw new IllegalArgumentException( "Key[" + (i >> 1) + "] is <null>." );
key = (K) keyOrValue;
continue;
case 1: // value
map.put( key, (V) keyOrValue );
}
return map;
}
}
public static void main( final String[] args ) {
System.out.println( InitMap.of( 1, "First", 2, "Second", 3, "Third" ) );
}
{1=First, 2=Second, 3=Third}
私は少し違うことをしました。最善ではないが、それは私のために働く。多分それは "一般化された"ことができます。
private static final Object[][] ENTRIES =
{
{new Integer(1), "one"},
{new Integer(2), "two"},
};
private static final Map myMap = newMap(ENTRIES);
private static Map newMap(Object[][] entries)
{
Map map = new HashMap();
for (int x = 0; x < entries.length; x++)
{
Object[] entry = entries[x];
map.put(entry[0], entry[1]);
}
return map;
}
ここにいくつかの良い答えがありますが、私はもう一つ申し出たいと思います。
Map
を作成し初期化するためのあなた自身の静的メソッドを作成してください。私は自分のCollectionUtils
クラスをパッケージ内に持っています。これは私が書くのが簡単で、より大きなライブラリへの依存の必要性を避けるために私が定期的に使う様々なユーティリティでプロジェクト間で使います。
これが私のnewMap
メソッドです。
public class CollectionUtils {
public static Map newMap(Object... keyValuePairs) {
Map map = new HashMap();
if ( keyValuePairs.length % 2 == 1 ) throw new IllegalArgumentException("Must have even number of arguments");
for ( int i=0; i<keyValuePairs.length; i+=2 ) {
map.put(keyValuePairs[i], keyValuePairs[i + 1]);
}
return map;
}
}
使用法:
import static CollectionUtils.newMap;
// ...
Map aMap = newMap("key1", 1.23, "key2", 2.34);
Map bMap = newMap(objKey1, objVal1, objKey2, objVal2, objKey3, objVal3);
// etc...
それは総称を利用しませんが、あなたが望むようにマップをタイプキャストすることができます(ちょうどあなたがそれを正しくタイプキャストすることを確認してください!)
Map<String,Double> aMap = (Map<String,Double>)newMap("key1", 1.23, "key2", 2.34);
2番目のメソッドは、必要に応じて保護されたメソッドを呼び出すことができます。これは、構築後に不変のクラスを初期化するのに役立ちます。