(文字どおりに)HashMapを直接初期化する方法は?


1094

このようなJava HashMapを初期化する方法はありますか?:

Map<String,String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};

正しい構文は何でしょうか?これについては何も見つかりませんでした。これは可能ですか?変更することなく、マップの作成時に事前にわかっている「最終/静的」値をマップに配置するための最短/最速の方法を探しています。



密接に関連:stackoverflow.com/questions/507602/…(両方の質問は、静的な最終値で定数マップを初期化することに関するものです。)
Jonik



apache.commons.collectionsを使用している場合は、commons.apache.org / proper / commons
axを

回答:


1344

すべてのバージョン

たった1つのエントリが必要な場合:がありますCollections.singletonMap("key", "value")

Javaバージョン9以降の場合:

はい、これは今可能です。Java 9では、マップの作成を簡素化する2つのファクトリメソッドが追加されました。

// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
    "a", "b",
    "c", "d"
);

// this works for any number of elements:
import static java.util.Map.entry;    
Map<String, String> test2 = Map.ofEntries(
    entry("a", "b"),
    entry("c", "d")
);

両方の上記の例のtesttest2ちょうど地図を表現する様々な方法を用いて、同じになります。Map.ofながら、この方法は、マップ内の10個の要素までのために定義されているMap.ofEntries方法は、そのような制限を持たないであろう。

この場合、結果のマップは不変のマップになることに注意してください。地図を変更可能にしたい場合は、たとえば、mutableMap = new HashMap<>(Map.of("a", "b"));

JEP 269Javadocも参照)

Javaバージョン8まで:

いいえ、すべての要素を手動で追加する必要があります。匿名サブクラスで初期化子を使用して、構文を少し短くすることができます。

Map<String, String> myMap = new HashMap<String, String>() {{
        put("a", "b");
        put("c", "d");
    }};

ただし、場合によっては、匿名サブクラスが望ましくない動作を引き起こす可能性があります。これには、たとえば次のものが含まれます。

  • メモリ消費、ディスク容量消費、起動時間を増加させる追加のクラスを生成します
  • 非静的メソッドの場合:作成メソッドが呼び出されたオブジェクトへの参照を保持します。つまり、作成されたマップオブジェクトがまだ参照されている間は、外部クラスのオブジェクトをガベージコレクションできないため、追加のメモリがブロックされます。

初期化に関数を使用すると、イニシャライザでマップを生成することもできますが、厄介な副作用は回避されます。

Map<String, String> myMap = createMap();

private static Map<String, String> createMap() {
    Map<String,String> myMap = new HashMap<String,String>();
    myMap.put("a", "b");
    myMap.put("c", "d");
    return myMap;
}

3
関数の要素を初期化したい場合、これは機能しません...
Michael

9
@Michael:ええ、そうです、もしあなたが関数を使いたいのであれば、not-functionを使うことはできません。しかし、なぜあなたはそうしたいのですか?
ヤンキー2015

6
そして、単一のエントリを持つマップが必要な場合のためにありますCollections.singletonMap():)
skwisgaar

3
安定したJava 9がリリースされたので、Javadocにはこのリンクを使用します。+1依存関係が1つ少ないため!
フランクリンYu

3
Java 9はどこにentry文書化されていますか?
nobar '27 / 07/27

1030

これは片道です。

HashMap<String, String> h = new HashMap<String, String>() {{
    put("a","b");
}};

ただし、注意して、上記のコードを理解する必要があります(HashMapから継承する新しいクラスを作成します)。したがって、http//www.c2.com/cgi/wiki?DoubleBraceInitialization で詳細を読む か、単にGuavaを使用する必要があります。

Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);

72
それは機能しますが、醜く、ユーザーが実行する前に理解する必要がある目に見えない副作用があります。たとえば、匿名クラス全体をその場で生成します。
jprete

96
うん、それは私が注意することについて書いて、説明へのリンクを与えた方法です。
gregory561 2011

6
素晴らしいリンク。GreencoddsTenthRuleOfProgrammingへのそのリンクの参照は一読の価値があります。
マイケルク2013年

19
"of"メソッドは最大5ペアに制限されているため、 "as ImmutableMap.builder.put(" k1 "、" v1 ")。put(" k2 "、" v2 ")。build()"を追加できますか?
kommradHomer 2014年


342

サードパーティのライブラリを許可する場合は、GuavaImmutableMapを使用して、リテラルのような簡潔さを実現できます。

Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");

これは最大5つのキーと値のペアで機能します。それ以外の場合は、そのビルダーを使用できます。

Map<String, String> test = ImmutableMap.<String, String>builder()
    .put("k1", "v1")
    .put("k2", "v2")
    ...
    .build();


  • GuavaのImmutableMap実装はJavaのHashMap実装とは異なることに注意してください(特に、不変であり、nullキー/値を許可していません)。
  • 詳細については、不変のコレクション型に関するGuavaのユーザーガイドの記事をご覧ください。

26
また、guavaにはImmutableMap.builder.put( "k1"、 "v1")。put( "k2"、 "v2")。build();があります。
Xetius 2013年

17
ImmutableMapはNull値で失敗するため、HashMapと同じではありませんが、マップHashMapは失敗しません。
Gewthen 14年

2
この問題に直面する可能性のある人を助けるためだけに。次のように、ビルダーを入力してMap <String、String>にする必要があります。Map<String、String> test = ImmutableMap。<String、String> builder()。put( "k1"、 "v1")。 put( "k2"、 "v2")。build();
チアゴ

これは素晴らしいイェンスです!
gaurav

105

これを行う直接的な方法はありません-JavaにはMapリテラルがありません(まだ-Java 8向けに提案されたと思います)。

このような人もいます:

Map<String,String> test = new HashMap<String, String>(){{
       put("test","test"); put("test","test");}};

これにより、HashMapの匿名サブクラスが作成され、そのインスタンス初期化子がこれらの値を配置します。(ちなみに、マップに同じ値を2回含めることはできません。2番目のputが最初のputを上書きします。次の例では異なる値を使用します。)

通常の方法はこれです(ローカル変数の場合):

Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");

あなたの場合はtestマップがインスタンス変数である、コンストラクタまたはインスタンス初期化子で初期化を置きます:

Map<String,String> test = new HashMap<String, String>();
{
    test.put("test","test");
    test.put("test1","test2");
}

あなたの場合はtestマップがクラス変数があり、静的初期化子で初期化を置きます:

static Map<String,String> test = new HashMap<String, String>();
static {
    test.put("test","test");
    test.put("test1","test2");
}

マップを変更しない場合は、初期化後にでマップをラップする必要がありますCollections.unmodifiableMap(...)。静的初期化子でもこれを行うことができます:

static Map<String,String> test;
{
    Map<String,String> temp = new HashMap<String, String>();
    temp.put("test","test");
    temp.put("test1","test2");
    test = Collections.unmodifiableMap(temp);
}

testファイナルにできるかどうかはわかりません...試して、ここで報告してください。)


61
Map<String,String> test = new HashMap<String, String>()
{
    {
        put(key1, value1);
        put(key2, value2);
    }
};

シンプルで要領を得ています。私はこれを拡張された解説セクションと一緒にすると最良の答えになると思います。
ooolala 2016年

15
ただし、注意すべきメモリの影響があります。blog.jooq.org/2014/12/08/...
Amalgovinus

1
@Amalgovinus基本的に、新しいサブクラスを作成することで、型引数HashMapをこのサブクラスにハードコーディングします。これは、実際に提供した場合にのみ機能します。(新しい(空の)HashMapでは、型引数は関係ありません。)
PaŭloEbermann

1
それの清浄度などのIが、それが不要な匿名クラスを作成し、問題はここで説明しています c2.com/cgi/wiki?DoubleBraceInitialization
ウダーチヌイ

1
@hello_its_me:stackoverflow.com/a/6802512/1386911の回答と同じであるため、フォーマットのみが異なります。そして、この場合、この拡張フォーマットは、読みやすさのためにコンパクトフォーマットに追加の価値がありません。
DanielHári16年

44

代わりに、プレーンなJava 7クラスとvarargsを使用してHashMapBuilder、このメソッドでクラスを作成します。

public static HashMap<String, String> build(String... data){
    HashMap<String, String> result = new HashMap<String, String>();

    if(data.length % 2 != 0) 
        throw new IllegalArgumentException("Odd number of arguments");      

    String key = null;
    Integer step = -1;

    for(String value : data){
        step++;
        switch(step % 2){
        case 0: 
            if(value == null)
                throw new IllegalArgumentException("Null key value"); 
            key = value;
            continue;
        case 1:             
            result.put(key, value);
            break;
        }
    }

    return result;
}

次のような方法を使用します。

HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");

私はあなたに触発答えを書いた:stackoverflow.com/questions/507602/...
GeroldBroserはモニカ復職

1
言及されたことはないが、以前のJavaバージョンを使用して読み取り可能なApache Utilsの別のソリューション:MapUtils.putAll(new HashMap <String、String>()、new Object [] {"My key"、 "my value"、...
ロリントクール

4

tl; dr

Map.of…Java 9以降のメソッドを使用します。

Map< String , String > animalSounds =
    Map.of(
        "dog"  , "bark" ,   // key , value
        "cat"  , "meow" ,   // key , value
        "bird" , "chirp"    // key , value
    )
;

Map.of

Java 9は一連のMap.of静的メソッドを追加して、希望どおりの処理を行いますリテラル構文を使用して不変Mapをインスタンス化します

マップ(エントリのコレクション)は不変なので、インスタンス化後にエントリを追加または削除することはできません。また、各エントリのキーと値は不変であり、変更できません。NULLを許可しない、重複キーを許可しない、マッピングの反復順序は任意など、他のルールについてはJavadoc参照してください

これらの方法を見てみましょう。サンプルデータを使用して、その日に作業する予定の人に対する曜日のマップのサンプルを使用します。

Person alice = new Person( "Alice" );
Person bob = new Person( "Bob" );
Person carol = new Person( "Carol" );

Map.of()

Map.of 空を作成します Map。変更できないため、エントリを追加できません。以下は、そのようなマップの例です。エントリはなく、空です。

Map < DayOfWeek, Person > dailyWorkerEmpty = Map.of();

dailyWorkerEmpty.toString():{}

Map.of( … )

Map.of( k , v , k , v , …)1〜10個のキーと値のペアを取得するいくつかのメソッドがあります。2つのエントリの例を次に示します。

Map < DayOfWeek, Person > weekendWorker = 
        Map.of( 
            DayOfWeek.SATURDAY , alice ,     // key , value
            DayOfWeek.SUNDAY , bob           // key , value
        )
;

weekendWorker.toString():{SUNDAY = Person {name = 'Bob'}、SATURDAY = Person {name = 'Alice'}}

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … )Map.Entryインターフェイスを実装する任意の数のオブジェクトを取ります。Javaはそのインターフェイス、1つの不定、他の不変を実装する2つのクラスをバンドル: AbstractMap.SimpleEntryAbstractMap.SimpleImmutableEntry。しかし、具体的なクラスを指定する必要はありません。Map.entry( k , v )メソッドを呼び出してキーと値を渡すだけで、実装しているクラスのオブジェクトを取得できますMap.Entryインターフェイスをます。

Map < DayOfWeek, Person > weekdayWorker = Map.ofEntries(
        Map.entry( DayOfWeek.MONDAY , alice ) ,            // Call to `Map.entry` method returns an object implementing `Map.Entry`. 
        Map.entry( DayOfWeek.TUESDAY , bob ) ,
        Map.entry( DayOfWeek.WEDNESDAY , bob ) ,
        Map.entry( DayOfWeek.THURSDAY , carol ) ,
        Map.entry( DayOfWeek.FRIDAY , carol )
);

weekdayWorker.toString():{WEDNESDAY = Person {name = 'Bob'}、TUESDAY = Person {name = 'Bob'}、THURSDAY = Person {name = 'Carol'}、FRIDAY = Person {name = 'Carol'} 、MONDAY = Person {name = 'Alice'}}

Map.copyOf

Java 10がメソッドを追加しましたMap.copyOf。既存のマップを渡し、そのマップの不変のコピーを取得します。

ノート

を介しMap.ofて作成されたマップの反復子の順序が保証されないことに。エントリには任意の順序があります。ドキュメントでは、注文が変更される可能性があることを警告しているため、表示されている注文に基づいてコードを記述しないでください。

これらのすべてのことに注意してくださいMap.of…メソッドが返すMap不特定のクラス。基礎となる具象クラスは、Javaのバージョンによって異なる場合があります。この匿名性により、Javaは、特定のデータに最適に適合するものであれば、さまざまな実装から選択できます。たとえば、キーがenumからのものである場合、Javaはで使用することEnumMapがあります。


1

Map.of2つの簡単な方法で、独自の(Java 9以降でのみ利用可能な)メソッドを簡単に作成できます。

設定された量のパラメーターで作成する

public <K,V> Map<K,V> mapOf(K k1, V v1, K k2, V v2 /* perhaps more parameters */) {
    return new HashMap<K, V>() {{
      put(k1, v1);
      put(k2,  v2);
      // etc...
    }};
}

リストを使用して作成する

特定のパラメータセットに対して多数のメソッドを作成する代わりに、リストを使用してこれを作成することもできます。

public <K, V> Map<K, V> mapOf(List<K> keys, List<V> values) {
   if(keys.size() != values.size()) {
        throw new IndexOutOfBoundsException("amount of keys and values is not equal");
    }

    return new HashMap<K, V>() {{
        IntStream.range(0, keys.size()).forEach(index -> put(keys.get(index), values.get(index)));
    }};
}

これを使用するたびに匿名クラスが作成されるため、これをすべてに使用することはお勧めしません。


1

JAVA 8

プレーンJava 8 Streams/Collectorsでは、ジョブを実行するために使用する可能性もあります。

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", "value2"),
         new SimpleEntry<>("key3", "value3"))
        .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue));

これには、匿名クラスを作成しないという利点があります。

インポートは次のとおりです。

import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;

もちろん、他の回答で述べられているように、Java 9以降では、同じことを行う簡単な方法があります。


0

残念ながら、キーと値のタイプが同じでない場合に可変引数を使用することはObject...、タイプセーフを完全に使用して失う必要があるため、あまり合理的ではありません。たとえばを常に作成したい場合Map<String, String>、もちろんa toMap(String... args)も可能ですが、キーと値を混同するのは簡単で、奇数の引数は無効になるため、あまりきれいではありません。

次のようなチェーン可能なメソッドを持つHas​​hMapのサブクラスを作成できます。

public class ChainableMap<K, V> extends HashMap<K, V> {
  public ChainableMap<K, V> set(K k, V v) {
    put(k, v);
    return this;
  }
}

そしてそれを new ChainableMap<String, Object>().set("a", 1).set("b", "foo")

別のアプローチは、一般的なビルダーパターンを使用することです。

public class MapBuilder<K, V> {
  private Map<K, V> mMap = new HashMap<>();

  public MapBuilder<K, V> put(K k, V v) {
    mMap.put(k, v);
    return this;
  }

  public Map<K, V> build() {
    return mMap;
  }
}

そしてそれを new MapBuilder<String, Object>().put("a", 1).put("b", "foo").build();

ただし、私が時々使用したソリューションでは、可変引数とPairクラスを使用しています。

public class Maps {
  public static <K, V> Map<K, V> of(Pair<K, V>... pairs) {
    Map<K, V> = new HashMap<>();

    for (Pair<K, V> pair : pairs) {
      map.put(pair.first, pair.second);
    }

    return map;
  }
}

Map<String, Object> map = Maps.of(Pair.create("a", 1), Pair.create("b", "foo");

煩わしさはPair.create()少し気になりますが、これでかなりうまくいきます。静的インポートを気にしない場合は、もちろんヘルパーを作成できます。

public <K, V> Pair<K, V> p(K k, V v) {
  return Pair.create(k, v);
}

Map<String, Object> map = Maps.of(p("a", 1), p("b", "foo");

Pairを使用することを想像する代わりにMap.Entry、それはインターフェースであるため、実装クラスまたはヘルパーファクトリメソッド、あるいはその両方が必要です。また、不変ではなく、このタスクに役立つ他のロジックが含まれています。)


0

Java 8でStreamsを使用できます(これはSetの例です)。

@Test
public void whenInitializeUnmodifiableSetWithDoubleBrace_containsElements() {
    Set<String> countries = Stream.of("India", "USSR", "USA")
      .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

    assertTrue(countries.contains("India"));
}

参照:https : //www.baeldung.com/java-double-brace-initialization


0

キーと値のペアを1つだけ配置する必要がある場合は、Collections.singletonMap(key、value);を使用できます。


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