HashMap的构建器


Answers:


20

由于Java 9 Map接口包含:

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

这些工厂方法的局限性在于:

  • 不能将nulls用作键和/或值(如果需要存储null,请查看其他答案)
  • 产生不变的地图

如果我们需要可变地图(例如HashMap),则可以使用其复制构造函数,并让其复制通过创建的地图内容Map.of(..)

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

2
请注意,Java 9方法不允许使用null值,根据使用情况,这可能是一个问题。
Per Lundberg

@JoshM。Map.of(k1,v1, k2,v2, ...)当我们没有太多价值时,可以安全地使用IMO 。对于较大的值,可以Map.ofEntries(Map.entry(k1,v1), Map.entry(k2,v2), ...)使我们更具可读性的代码更少出错(除非我误解了您)。
Pshemo

你没事的 前者对我来说真是毛骨悚然。我拒绝使用它!
Josh M.

164

HashMaps没有这种东西,但是您可以使用构建器创建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
ImmutableMap不支持null值。因此,这种方法有局限性:您不能在HashMapto中设置值null
vitaly

5
肖恩·帕特里克·弗洛伊德(sean-patrick-floyd),这是一个实际示例:Spring的NamedParameterJdbcTemplate期望由参数名称作为键的值的映射。假设我想使用NamedParameterJdbcTemplate将列值设置为null。我看不到:a)它是一种代码气味;b)如何在此处使用空对象模式
vitaly

2
@vitaly对此无法抗拒
肖恩·帕特里克·弗洛伊德

2
使用new HashMapJava构造函数而不是静态Maps.newHashMap方法有什么问题吗?
CorayThan

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

3
@Elazar map.getClass()==HashMap.class将返回false。但这还是一个愚蠢的考验。HashMap.class.isInstance(map)应该是首选,这将返回true。
肖恩·帕特里克·弗洛伊德

59
那就是说:我仍然认为这种解决方案是邪恶的。
肖恩·帕特里克·弗洛伊德

11
这是一个实例初始化程序,而不是静态初始化程序。对于类中的每个构造函数,它在super的构造函数之后但在构造函数的主体之前运行。生命周期不是很广为人知,因此我避免了这种习惯用法。
Joe Coder

14
这是一个非常糟糕的解决方案,应避免使用:stackoverflow.com/a/27521360/3253277
Alexandre DuBreuil

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 2011年

与构建器相同,但是数据量有限,因为它是通过重载实现的。如果您只有几个元素-我想这是可取的。
Elazar Leibovich

4

HashMap易变 不需要建造者。

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

如果要使用它初始化字段怎么办?同一行中的所有逻辑都比散布在场和c'tor之间的逻辑更好。
Elazar Leibovich

@Elazar:如果要使用这样的在编译时已知的特定值初始化字段,则通常希望该字段是不可变的,应该使用ImmutableSet。如果您确实希望它是可变的,则可以在构造函数或实例初始化程序块或静态初始化程序块(如果它是静态字段)中对其进行初始化。
ColinD 2011年

1
嗯,ImmutableMap显然应该在那里说。
ColinD 2011年

我不这么认为。我宁愿在相同的定义行中看到初始化,然后将它们塞进非静态的初始化中{{init();}}(而不是在构造函数中,因为其他构造函数可能会忘记它)。很高兴它是原子动作。如果地图易失,请使用构建器对其进行初始化,以确保它始终处于null或处于最终状态,永远不会填满一半。
Elazar Leibovich

1

您可以在Eclipse Collections中使用流畅的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 Collections的提交者。


0

不久前,我也有类似的要求。它与番石榴无关,但是您可以执行类似的操作,以便能够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;
    }
}

然后使用适合您需求的方法来创建流利的构建器:

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}]}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.