静的マップをどのように初期化できますか?


1131

MapJavaでstaticをどのように初期化しますか?

方法1:静的初期化子
方法2:インスタンス初期化子(匿名サブクラス)またはその他のメソッド?

それぞれの長所と短所は何ですか?

2つの方法を示す例を次に示します。

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

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}

2
Java 8でマップを初期化する場合:stackoverflow.com/a/37384773/1216775
akhil_mittal

2
ダブルブレースの初期化は絶対に使用しないでください。これはハックであり、メモリリークやその他の問題を引き起こす簡単な方法です。
dimo414 2017年

Java 9?エントリ数が10以下の場合はMap.ofelseを使用しMap.ofEntriesstackoverflow.com
a / 37384773/1216775を

回答:


1106

この場合、インスタンスのイニシャライザは単に構文上の砂糖ですよね?初期化するためだけに追加の匿名クラスが必要な理由はわかりません。そして、作成されるクラスがfinalの場合は機能しません。

静的イニシャライザを使用して不変マップを作成することもできます:

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

10
これは私が長年使ってきたイディオムで、誰もそれを見たことがありませんでした。変更不可能な定数のセットとリストについても同様です。
jasonmp85

3
文字列キーでHashMap <String、String>をどのように処理しますか?Mapオブジェクトでは文字列キーを使用できないため、unmodifiableMap()を使用できません。HashMapにキャストすると、目的も無効になると思います。何か案は?
ルーク

30
@Luke Androidにこのような制限があることを真剣に疑っています。それはまったく意味がありません。クイック検索でこの質問(および他の多くの質問)が見つかりました。これは、AndroidのMapオブジェクトに文字列キーを使用できることを意味しているようです。
mluisbrown

11
ですから、他の誰も気にせずに調査します。AndroidのMapオブジェクトに文字列キーを使用しても問題がないことを確認できます。
ジョーダン

11
ジョーダン:今は古いトピックですが、@ LukeがMap <Integer、String>のように、キータイプが異なるマップのキーとして文字列を使用しようとしていたと思います。
悲惨な変数

445

静的で不変のマップを初期化する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の不変コレクションユーティリティの利点の詳細については、「Guava User Guide」のImmutable Collections Explained」を参照してください。

グアバ(のサブセット)は、以前はGoogleコレクションと呼ばれていました。このライブラリをJavaプロジェクトでまだ使用していない場合は、ぜひお試しください。Guavaは、SOの仲間のユーザーが同意するように、Javaで最も人気があり便利な無料のサードパーティライブラリの1つになりました。(これに慣れていない場合は、そのリンクの背後にいくつかの優れた学習リソースがあります。)


アップデート(2015)Java 8に関しては、他の方法よりもずっとクリーンなので、私はまだGuavaアプローチを使用します。Guavaに依存したくない場合は、単純な古いinitメソッドを検討してください。2次元配列とストリームAPIを使用したハックは、私に尋ねるとかなり醜く、キーと値が同じ型ではない(Map<Integer, String>質問のように)マップを作成する必要がある場合は醜くなります。

グアバの将来については一般的に、Java 8に関して、ルイスワッサーマンは2014年にこれを述べ、2016年に[ 更新 ] グアバ21がJava 8を必要とし、適切にサポートすることが発表されました。


更新(2016)Tagir Valeevが指摘しているようにJava 9はコレクションに便利なファクトリーメソッドを追加することにより、純粋なJDKのみを使用してこれを最終的にクリーンにします。

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);

21
私たちの仲間のSO管理者は、私がリンクした由緒ある「最も有用な無料のサードパーティJavaライブラリ」の質問を削除したようです。:(それらを酷評しなさい。
Jonik

2
これは、定数マップを初期化する最も良い方法です。さらに読みやすくなるだけでなく、Collections.unmodifiableMapは基になるマップの読み取り専用ビューを返します(変更可能です)。
クランチドッグ

11
削除された質問(1万件以上の担当者)が表示されるようになったので、「最も便利な無料のサードパーティJavaライブラリ」のコピーを示します。これは最初のページにすぎませんが、少なくとも上記のグアバのリソースを見つけることができます。
Jonik 2013

2
私はこのアプローチを本当に好みますが、余分な依存関係なしでそれを行う方法を知ることは有益です。
レンチ

2
JEP 186はまだクローズされていないため、コレクションリテラルに関連する新機能が導入される可能性があります
cybersoft

182

私は使うだろう:

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
  1. 個人的に悪いスタイルだと私が考える匿名クラスを避け、
  2. マップの作成をより明確にします
  3. マップを変更できなくなります
  4. MY_MAPは定数なので、定数のように名前を付けます

3
純粋なJDKオプション(libsなし)の中で、マップ定義がその初期化に明確にリンクされているため、私はこれが最も好きです。定数の命名についても合意しました。
Jonik、2013

あなたがこれをできるとは思いもしなかった。
romulusnr

181

Java 5は、このよりコンパクトな構文を提供します。

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

46
その技術は、ダブルブレースの初期化と呼ばれる:stackoverflow.com/questions/1372113/...それは特別なJava 5の構文ではありません、それはインスタンス初期化子と匿名クラスでちょうどトリックです。
Jesper

13
ダブルブレースの初期化に関する簡単な質問:これを行うと、Eclipseは欠落しているシリアルIDに関する警告を発行します。一方では、この特定のケースでシリアルIDが必要になる理由はわかりませんが、他方では、通常、警告を抑制したくありません。これについてどう思いますか?
nbarraille

8
ためだ@nbarraille HashMap implements Serializable。この「トリック」を使用してHashMapのサブクラスを実際に作成するので、暗黙的にSerializableクラスを作成します。このため、serialUIDを指定する必要があります。
2013年

5
Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes.– IntelliJ
Mark Jeronimus 2017

3
@MarkJeronimus-推奨される使用法静的コンテキストです。パフォーマンスが低下する可能性がありますが、静的に定義されたマップの数が少ないと思われる場合は、それほど顕著ではありません。 Mapの任意のサブクラスでHashMap.equals定義されAbstractMapて機能するため、ここでは問題になりません。ダイヤモンドオペレーターは煩わしいですが、前述のように解決されました。
ジュール

95

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!

3
新しい演算子を静的{}ブロックに移動してラップすることにより、最初のメソッドでこれを簡単に行うことはできませんか?
Patrick、

2
とにかく、コンストラクタの呼び出しを初期化されたstaticに移動します。それ以外は奇妙に見えます。
トム・ホーティン-2009

2
具体的なクラスとは対照的に匿名クラスを使用することでどのようなパフォーマンスヒットが見られるかについての考え
キップ

62

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>質問のようにaを初期化するには、次のようなものが必要です:

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

Edit(2):new SimpleEntry<>(k, v)呼び出しのストリームを使用する、i_am_zeroによるより良い混合型対応バージョンがあります。その答えを確認してください:https : //stackoverflow.com/a/37384773/3950982


7
質問と他の回答と同等のバージョンを自由に追加しました。キーと値のタイプが異なるMapを初期化します(そうしString[][]ないと、Object[][]必要になります)。私見、このアプローチは醜く(キャストの場合はなおさら)、覚えにくいです。自分では使用しないでしょう。
Jonik

57

Map.of Java 9以降

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

詳細については、JEP 269を参照してください。JDK 9は2017年9月に一般提供を開始しました。


7
または、10を超えるキーと値のペアが必要な場合は、使用できますMap.ofEntries
ZhekaKozlov

8
あなたが実現するまでは、清潔ですべてです、それが実装された方法
半ば

残念なことに、10エントリしかサポートしていないようです。その後はofEntriesを使用する必要があります。ラメ。
Somaiah Kumbera

2
JDKの実装のクリーン度は、それが機能し、契約を満たしている限り、問題になりません。他のブラックボックスと同様に、本当に必要に応じて、実装の詳細をいつでも修正できます...
vikingsteve

@midこれは、Javaでこれを行う唯一のタイプセーフな方法です。
ルークハッチソン

44

Java 9

を使用Map.ofEntriesして呼び出しMap.entry( k , v )、各エントリを作成できます。

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

ここMap.ofでの彼の回答示唆されているように、Tagirが使用することもできますが、を使用して10エントリを超えることはできませんMap.of

Java 8(きちんとしたソリューション)

マップエントリのストリームを作成できます。我々はすでにの2つの実装持ちEntryjava.util.AbstractMapあるSimpleEntrySimpleImmutableEntryを。この例では、前者を次のように使用できます。

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

2
new SimpleEntry<>()方法は、静的よりもはるかに少ない読めるput()/:
ダノン

32

Eclipseのコレクションは、次のすべてが動作します:

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コレクションのコミッターです


1
EclipseコレクションがJavaのデフォルトのコレクションライブラリであることを本当に願っています。Guava + JCLよりもずっと楽しんでいます。
ケニーケイソン

29

この状況では、匿名サブクラスを作成することはありません。たとえば、マップを変更不可にしたい場合は、静的初期化子も同様に機能します。

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

1
では、匿名サブクラスを使用してハッシュマップを初期化する状況はどれですか。
dogbane

6
コレクションを初期化しないでください。
eljenso 2009

静的な初期化子を使用する方が、無名のサブクラスを作成するよりも良い選択である理由を説明できますか?
leba-lev 2012

3
@rookie静的initを支持する他の回答にはいくつかの理由があります。ここの目的初期化することです。それで、おそらくいくつかのキーストロークを保存することを除いて、なぜサブクラス化を取り入れますか?(キーストロークを節約したい場合、Javaはプログラミング言語としては決して良い選択ではありません。)私がJavaでプログラミングするときに使用する経験則の1つは、サブクラスをできるだけ少なくすることです(合理的に回避できる場合は絶対にしないでください)。
eljenso 2012年

@eljenso-私が一般的にサブクラス構文を使用する理由は、初期化をインライン化するためです。次善の選択は、初期化されたマップを返す静的メソッドを呼び出すことです。しかし、私はあなたのコードを見て、MY_MAPがどこから来るのかを数秒間費やす必要があるのではないかと心配しています。それは、無駄にしたくない時間です。読みやすさの向上はおまけであり、パフォーマンスへの影響は最小限であるため、これは私にとって最良のオプションのようです。
ジュール、

18

多分それは彼らが彼らのページに持っているビデオなどのGoogleコレクションをチェックするのは面白いです。マップとセットを初期化するさまざまな方法を提供し、不変のコレクションも提供します。

更新:このライブラリの名前はGuavaになりました


17

それを扱うのは簡単なので、私は匿名クラスが好きです:

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});

12
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を使用することをお勧めします。そうしないと、定数として処理できません。


10

静的ブロックスタイルよりも「ダブルブレース初期化」スタイルを強くお勧めします。

誰かが匿名クラス、オーバーヘッド、パフォーマンスなどを好まないとコメントするかもしれません。

しかし、私がより考慮すべきことは、コードの可読性と保守性です。この観点から、私は、二重ブレースが静的メソッドよりも優れたコードスタイルであると思います。

  1. 要素はネストされ、インライン化されます。
  2. 手続き型ではなく、オブジェクト指向です。
  3. パフォーマンスへの影響は非常に小さく、無視できます。
  4. IDEのアウトラインサポートの向上(多くの匿名静的{}ブロックではなく)
  5. あなたはそれらの関係をもたらすためにコメントの数行を保存しました。
  6. 初期化されていないオブジェクトのエレメントリーク/インスタンスリードが例外およびバイトコードオプティマイザから発生する可能性を防止します。
  7. 静的ブロックの実行順序を気にする必要はありません。

さらに、匿名クラスのGCを認識している場合は、を使用していつでも通常のHashMapに変換できますnew HashMap(Map map)

別の問題に直面するまで、これを行うことができます。その場合は、完全な別のコーディングスタイル(静的でないファクトリクラスなど)を使用する必要があります。


8

通常どおり、apache-commonsには適切なメソッドMapUtils.putAll(Map、Object [])があります。

たとえば、カラーマップを作成するには:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

私はすべてのビルドにApache Commonsを含めているので、Arrays.asMap( ... )プレーンJavaにメソッドが存在しないという残念なことに、これが最善の解決策だと思います。車輪の再発明は通常ばかげています。非常にわずかな欠点は、ジェネリックスではチェックされていない変換が必要になることです。
マイクげっ歯類

@mikerodent 4.1バージョンは一般的です:public static <K、V> Map <K、V> putAll(final Map <K、V> map、final Object [] array)
agad

Tx ...はい、私は4.1を使用していますがSuppressWarnings( unchecked )、Eclipseで次のような行を使用する必要がありますMap<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
マイクげっ歯類

@mikerodentはObject [] []が原因ですよね?更新されたunswearを参照してください-Eclipseで警告はありません。
再度、

なんと奇妙なことでしょう...私が行ってもString[][]「警告」が表示されます。そしてもちろん、KそれVがあなたとあなたのクラスが同じ場合にのみ機能します。私はあなたがあなたのEclipse設定で「理解できない」に「チェックされていない変換」を「無視」に設定していないと思いますか?
マイクげっ歯類

7

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

7

変更不可能なマップが必要な場合は、最後にJava 9 ofMapインターフェースにクールなファクトリーメソッドを追加しました。同様のメソッドがSet、Listにも追加されています。

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");


6

匿名のクラス(それ以上の目的はない)を生成しないようにするために、静的初期化子を使用することを好むので、静的初期化子で初期化するヒントをリストします。リストされているすべての解決策/ヒントはタイプセーフです。

注:この質問では、マップを変更不可にすることについては何も述べられていないため、省略しますが、で簡単に実行できることを知っていますCollections.unmodifiableMap(map)

最初のヒント

最初のヒントは、マップへのローカル参照を作成し、それに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番目のヒント

2番目のヒントは、エントリを追加するヘル​​パーメソッドを作成できることです。また、必要に応じて、このヘルパーメソッドをパブリックにすることもできます。

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番目のヒント

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

5

作成している匿名クラスはうまく機能します。ただし、これは内部クラスであるため、周囲のクラスインスタンスへの参照が含まれることに注意してください。そのため、(XStreamを使用して)特定のことができないことがわかります。非常に奇妙なエラーが発生します。

そうは言っても、あなたが知っている限り、このアプローチは問題ありません。ほとんどの場合、あらゆる種類のコレクションを簡潔に初期化するために使用します。

編集:これは静的クラスであることをコメントで正しく指摘しました 明らかに私はこれを十分によく読みませんでした。ただし、私のコメントまだ匿名の内部クラスに適用されます。


3
この特定のケースでは静的なので、外部インスタンスはありません。
トム・ホーティン-2009

間違いなく、XStreamはこのようなものをシリアル化しようとしてはなりません(静的です。静的変数をシリアル化する必要があるのはなぜですか?)
jasonmp85

5

簡潔で比較的安全なものが必要な場合は、コンパイル時の型チェックをランタイムにシフトするだけです。

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

4

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
  • タイプセーフであり、キーと値のさまざまなタイプに簡単に対応できます。

必要に応じて、toMapマップサプライヤーを含む署名を使用して、マップのタイプを指定できます。
zrvan 2015年


4

あなたは使用することStickyMapMapEntryからCactoos

private static final Map<String, String> MAP = new StickyMap<>(
  new MapEntry<>("name", "Jeffrey"),
  new MapEntry<>("age", "35")
);

4

2番目のアプローチ(ダブルブレースの初期化)アンチパターンと考えられていますであると考えられるため、最初のアプローチを採用します。

静的マップを初期化する別の簡単な方法は、このユーティリティ関数を使用することです。

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

注:Map.ofJava 9を使用できます


3

静的イニシャライザ構文が好きではないので、匿名サブクラスに確信が持てません。一般に、静的イニシャライザを使用することのすべての短所と、以前の回答で言及された匿名サブクラスを使用することのすべての短所に同意します。一方、これらの投稿で提示されたプロは私には十分ではありません。私は静的初期化メソッドを使用することを好みます:

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

3

私が使用する(そして好きになるようになっている)アプローチが回答に投稿されていないので、ここに示します。

静的なイニシャライザは扱いにくいので使用したくありません。また、インスタンスごとに新しいクラスを作成するため、匿名クラスは好きではありません。

代わりに、次のような初期化を好みます。

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オブジェクトの初期化ほど良くありませんが、読みやすさに関しては、その方向へのステップです。


3

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}

一度に要素をマップに作成して移入するよりもはるかに便利です。


2

JEP 269は、コレクションAPIのいくつかの便利なファクトリメソッドを提供します。このファクトリメソッドは現在のJavaバージョン(8)にはありませんが、Java 9リリースで計画されています。

以下のためにMapそこに2ファクトリメソッドは、次のとおりofofEntries。を使用するとof、キーと値のペアを交互に渡すことができます。たとえば、いいMapね!を作成するには{age: 27, major: cs}

Map<String, Object> info = Map.of("age", 27, "major", "cs");

現在、には10個のオーバーロードバージョンがあるためof、10個のキーと値のペアを含むマップを作成できます。この制限または交互のキー/値が気に入らない場合は、次を使用できますofEntries

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

ofofEntriesはどちらも不変を返すMapため、構築後にそれらの要素を変更することはできません。JDK 9 Early Accessを使用してこれらの機能を試すことができます。


2

ええと...私は列挙型が好きです;)

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

2

答えを読んだので、自分でマップビルダーを作成することにしました。コピー&ペーストしてお楽しみください。

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かなり頻繁におり、私はそれがちょっと好きです。これをコードに追加してコンストラクタをプライベートにし、静的なファクトリメソッドパターンに切り替えました。

EDIT2:さらに最近では、of静的インポートを使用するとかなり悪いように見えるため、と呼ばれる静的メソッドはもはや好きではありません。mapOf代わりにに名前を変更して、静的インポートにより適したものにしました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.