HashMapのビルダー


回答:


20

Java 9 Mapインターフェースには以下が含まれているため:

  • Map.of(k1,v1, k2,v2, ..)
  • Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ..)

これらのファクトリメソッドの制限は、次のとおりです。

  • nullキーや値としてsを保持できない(nullを格納する必要がある場合は、他の回答を見てください)
  • 生産する不変のマップを

(HashMapのような)変更可能なマップが必要場合は、そのコピーコンストラクターを使用して、作成したマップのコンテンツをコピーできます。Map.of(..)

Map<Integer, String> map = new HashMap<>( Map.of(1,"a", 2,"b", 3,"c") );

2
Java 9メソッドはnull値を許可しないことに注意してください。これは、ユースケースによっては問題になる可能性があります。
Lundberg氏による

@JoshM。IMO Map.of(k1,v1, k2,v2, ...)は、値が少ない場合でも安全に使用できます。値の量が多いほど、Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)コードが読みやすくなり、エラーが発生しにくくなります(誤解しない限り)。
Pshemo

わかったよ 前者は私には本当にひどいです。使用を拒否します!
Josh M.

164

HashMapにはそのようなものはありませんが、ビルダーを使用してImmutableMapを作成できます。

final Map<String, Integer> m = ImmutableMap.<String, Integer>builder().
      put("a", 1).
      put("b", 2).
      build();

変更可能なマップが必要な場合は、それをHashMapコンストラクターにフィードするだけです。

final Map<String, Integer> m = Maps.newHashMap(
    ImmutableMap.<String, Integer>builder().
        put("a", 1).
        put("b", 2).
        build());

43
ImmutableMapnull値をサポートしていません。したがって、このアプローチの限界があります:あなたは、あなたに値を設定することはできませんHashMapnull
バイタリー2013年

5
sean-patrick-floydさて、実用的な例の1つ:SpringのNamedParameterJdbcTemplateは、パラメーター名でキー設定された値のマップを想定しています。NamedParameterJdbcTemplateを使用して列の値をnullに設定するとします。わからない:a)コードの匂い。b)ここでnullオブジェクトパターンを使用する方法
バイタリー2013

2
@vitalyはそれについて議論することはできません
ショーンパトリックフロイド

2
new HashMap静的Maps.newHashMapメソッドの代わりにJavaコンストラクタを使用することに問題はありますか?
CorayThan 2013年

1
@CorayThan-Jonikは正解です。これは、静的型推論に依存する単なるショートカットです。stackoverflow.com/a/13153812
AndersDJohnson

46

まったくビルダーではありませんが、初期化子を使用します。

Map<String, String> map = new HashMap<String, String>() {{
    put("a", "1");
    put("b", "2");
}};

待つ。それはmap instanceof HashMap間違いではないでしょうか?それほど素晴らしいアイデアではないようです。
Elazar Leibovich、2009

3
@Elazar map.getClass()==HashMap.classはfalseを返します。しかし、それはとにかく愚かなテストです。HashMap.class.isInstance(map)優先する必要があり、それはtrueを返します。
Sean Patrick Floyd、

59
それは言った:私はまだこの解決策は悪だと思います。
Sean Patrick Floyd

11
これはインスタンス初期化子であり、静的初期化子ではありません。これは、クラスのすべてのコンストラクターについて、スーパーのコンストラクターの後で、コンストラクターの本体の前に実行されます。ライフサイクルはあまり知られていないため、このイディオムは避けます。
Joe Coder、2012年

14
これは非常に悪いソリューションであり、避けるべきです:stackoverflow.com/a/27521360/3253277
アレクサンドルデュブルイユ

36

これは受け入れられた回答に似ていますが、私の見解では少しクリーンです:

ImmutableMap.of("key1", val1, "key2", val2, "key3", val3);

上記の方法にはいくつかのバリエーションがあり、静的で不変の不変マップを作成するのに最適です。


4
私はビルダーを求めました。少数の要素に制限されます。
Elazar Leibovich 2014

見た目もすっきりしていますが、Perlの=>演算子が長くなるのは不思議です。
アーロンマエンパー

10

これは非常にシンプルなものです...

public class FluentHashMap<K, V> extends java.util.HashMap<K, V> {
  public FluentHashMap<K, V> with(K key, V value) {
    put(key, value);
    return this;
  }

  public static <K, V> FluentHashMap<K, V> map(K key, V value) {
    return new FluentHashMap<K, V>().with(key, value);
  }
}

その後

import static FluentHashMap.map;

HashMap<String, Integer> m = map("a", 1).with("b", 2);

https://gist.github.com/culmat/a3bcc646fa4401641ac6eb01f3719065を参照してください


シンプルなアプローチが気に入っています。特に、これは2017年(現在はほぼ2018年です)であり、JDKにはそのようなAPIはまだありません
Milad Naseri

本当にクールに見えます、ありがとう。@MiladNaseri JDKのAPIにまだこのようなものが含まれていないのは正気ではありません。
そうもない

9

簡単なマップビルダーを書くのは簡単です。

public class Maps {

    public static <Q,W> MapWrapper<Q,W> map(Q q, W w) {
        return new MapWrapper<Q, W>(q, w);
    }

    public static final class MapWrapper<Q,W> {
        private final HashMap<Q,W> map;
        public MapWrapper(Q q, W w) {
            map = new HashMap<Q, W>();
            map.put(q, w);
        }
        public MapWrapper<Q,W> map(Q q, W w) {
            map.put(q, w);
            return this;
        }
        public Map<Q,W> getMap() {
            return map;
        }
    }

    public static void main(String[] args) {
        Map<String, Integer> map = Maps.map("one", 1).map("two", 2).map("three", 3).getMap();
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }
}

6

以下を使用できます。

HashMap<String,Integer> m = Maps.newHashMap(
    ImmutableMap.of("a",1,"b",2)
);

上品で読みやすいものではありませんが、機能します。


1
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);より良いですか?
lschin

ビルダーと同じですが、オーバーロードで実装されているため、データ量が限られています。要素が少ない場合は、それが望ましいと思います。
Elazar Leibovich、2009

4

HashMap変更可能です。ビルダーは必要ありません。

Map<String, Integer> map = Maps.newHashMap();
map.put("a", 1);
map.put("b", 2);

それでフィールドを初期化したい場合はどうでしょうか?同じ行のすべてのロジックは、フィールドとコントローラーの間に散在するロジックよりも優れています。
Elazar Leibovich、2009

@Elazar:このように、コンパイル時に既知の特定の値でフィールドを初期化する場合、通常はそのフィールドを不変にして、を使用する必要がありますImmutableSet。本当に変更可能にする場合は、コンストラクターで初期化するか、インスタンス初期化ブロックまたは静的フィールドの場合は静的初期化ブロックで初期化できます。
ColinD 2011

1
えっと、ImmutableMap明らかにそこに言っておくべきだった。
ColinD

そうは思いません。同じ定義の行で初期化を見て、それらを非静的な初期化に含めたいと思います{{init();}}(他のコンストラクターが忘れる可能性があるため、コンストラクターではありません)。そして、それが一種のアトミックアクションであることは素晴らしいことです。マップが揮発性の場合、ビルダーでマップを初期化することで、マップが常にnullまたは最終状態になり、半分塗りつぶされないようにします。
Elazar Leibovich、2009

1

Eclipseコレクションで Fluent APIを使用できます。

Map<String, Integer> map = Maps.mutable.<String, Integer>empty()
        .withKeyValue("a", 1)
        .withKeyValue("b", 2);

Assert.assertEquals(Maps.mutable.with("a", 1, "b", 2), map);

詳細と例を掲載したブログはこちらです。

注:私はEclipseコレクションのコミッターです。


0

しばらく前に同様の要件がありました。Guavaとは関係ありませんがMap、流暢なビルダーを使用してをきれいに構築できるように、このようなことを行うことができます。

Mapを拡張する基本クラスを作成します。

public class FluentHashMap<K, V> extends LinkedHashMap<K, V> {
    private static final long serialVersionUID = 4857340227048063855L;

    public FluentHashMap() {}

    public FluentHashMap<K, V> delete(Object key) {
        this.remove(key);
        return this;
    }
}

次に、ニーズに合ったメソッドを使用してFluent Builderを作成します。

public class ValueMap extends FluentHashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    public ValueMap() {}

    public ValueMap withValue(String key, String val) {
        super.put(key, val);
        return this;
    }

... Add withXYZ to suit...

}

その後、次のように実装できます。

ValueMap map = new ValueMap()
      .withValue("key 1", "value 1")
      .withValue("key 2", "value 2")
      .withValue("key 3", "value 3")

0

これは、特にテストフィクスチャをセットアップするときに、私が常に望んでいたことです。最後に、Mapの実装を構築できる独自の流暢なビルダーを作成することにしました -https://gist.github.com/samshu/b471f5a2925fa9d9b718795d8bbdfe42#file-mapbuilder-java

    /**
     * @param mapClass Any {@link Map} implementation type. e.g., HashMap.class
     */
    public static <K, V> MapBuilder<K, V> builder(@SuppressWarnings("rawtypes") Class<? extends Map> mapClass)
            throws InstantiationException,
            IllegalAccessException {
        return new MapBuilder<K, V>(mapClass);
    }

    public MapBuilder<K, V> put(K key, V value) {
        map.put(key, value);
        return this;
    }

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

0

これが私が書いたものです

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public class MapBuilder<K, V> {

    private final Map<K, V> map;

    /**
     * Create a HashMap builder
     */
    public MapBuilder() {
        map = new HashMap<>();
    }

    /**
     * Create a HashMap builder
     * @param initialCapacity
     */
    public MapBuilder(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }

    /**
     * Create a Map builder
     * @param mapFactory
     */
    public MapBuilder(Supplier<Map<K, V>> mapFactory) {
        map = mapFactory.get();
    }

    public MapBuilder<K, V> put(K key, V value) {
        map.put(key, value);
        return this;
    }

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

    /**
     * Returns an unmodifiable Map. Strictly speaking, the Map is not immutable because any code with a reference to
     * the builder could mutate it.
     *
     * @return
     */
    public Map<K, V> buildUnmodifiable() {
        return Collections.unmodifiableMap(map);
    }
}

次のように使用します。

Map<String, Object> map = new MapBuilder<String, Object>(LinkedHashMap::new)
    .put("event_type", newEvent.getType())
    .put("app_package_name", newEvent.getPackageName())
    .put("activity", newEvent.getActivity())
    .build();

0

Java 8の使用:

これはJava-9のアプローチです Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)

public class MapUtil {
    import static java.util.stream.Collectors.toMap;

    import java.util.AbstractMap.SimpleEntry;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.stream.Stream;

    private MapUtil() {}

    @SafeVarargs
    public static Map<String, Object> ofEntries(SimpleEntry<String, Object>... values) {
        return Stream.of(values).collect(toMap(Entry::getKey, Entry::getValue));
    }

    public static SimpleEntry<String, Object> entry(String key, Object value) {
        return new SimpleEntry<String, Object>(key, value);
    }
}

使い方:

import static your.package.name.MapUtil.*;

import java.util.Map;

Map<String, Object> map = ofEntries(
        entry("id", 1),
        entry("description", "xyz"),
        entry("value", 1.05),
        entry("enable", true)
    );


0

Underscore-javaはハッシュマップを作成できます。

Map<String, Object> value = U.objectBuilder()
        .add("firstName", "John")
        .add("lastName", "Smith")
        .add("age", 25)
        .add("address", U.arrayBuilder()
            .add(U.objectBuilder()
                .add("streetAddress", "21 2nd Street")
                .add("city", "New York")
                .add("state", "NY")
                .add("postalCode", "10021")))
        .add("phoneNumber", U.arrayBuilder()
            .add(U.objectBuilder()
                .add("type", "home")
                .add("number", "212 555-1234"))
            .add(U.objectBuilder()
                .add("type", "fax")
                .add("number", "646 555-4567")))
        .build();
    // {firstName=John, lastName=Smith, age=25, address=[{streetAddress=21 2nd Street,
    // city=New York, state=NY, postalCode=10021}], phoneNumber=[{type=home, number=212 555-1234},
    // {type=fax, number=646 555-4567}]}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.