HashMap 용 빌더


답변:


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값을 허용하지 않으므로 사용 사례에 따라 문제가 될 수 있습니다.
— 당 룬드 버그

@JoshM. IMO Map.of(k1,v1, k2,v2, ...)는 값이 많지 않을 때 안전하게 사용할 수 있습니다. 값 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
ImmutableMapnull값을 지원하지 않습니다 . 그래서이 방법의 제한이 : 당신은 당신의 값을 설정할 수 없습니다 HashMap에 null.
— vitaly

5
sean-patrick-floyd 한 가지 실용적인 예 : Spring의 NamedParameterJdbcTemplate 은 매개 변수 이름으로 키가 지정된 값의 맵을 예상합니다. NamedParameterJdbcTemplate을 사용하여 열 값을 null로 설정한다고 가정 해 보겠습니다. 나는 보지 않는다 : a) 그것이 어떻게 코드 냄새인지; b) 여기서 null 개체 패턴을 사용하는 방법
— vitaly

2
@vitaly 그 논쟁 없습니다
— 숀 패트릭 플로이드

2
new HashMap정적 Maps.newHashMap메서드 대신 Java 생성자 를 사용하는 데 문제가 있습니까?
— CorayThan

1
@CorayThan-Jonik이 맞습니다. 정적 유형 추론에 의존하는 지름길 일뿐입니다. stackoverflow.com/a/13153812
— AndersDJohnson 2015 년

46

빌더는 아니지만 이니셜 라이저를 사용합니다.

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

기다림. 그것이 map instanceof HashMap거짓이 되지 않을까요 ? 그다지 좋지 않은 아이디어처럼 보입니다.
— Elazar Leibovich 2011 년

3
@Elazar map.getClass()==HashMap.class는 false를 반환합니다. 그러나 그것은 어쨌든 어리석은 테스트입니다. HashMap.class.isInstance(map)선호되어야하며 true를 반환합니다.
— Sean Patrick Floyd

59
즉, 여전히이 솔루션이 악하다고 생각합니다.
— Sean Patrick Floyd

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의 => 연산자를 그리워합니다 ... 이상한 느낌입니다.
— Aaron Maenpaa

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를


나는 당신의 접근 방식의 단순함을 좋아합니다. (! 지금은 거의 2018)이 2017 년이며, JDK에서 이러한 API는 여전히 존재 특히 때문에
— 밀라 드 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 2011 년

4

HashMap변경 가능합니다. 빌더가 필요하지 않습니다.

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

필드를 초기화하려면 어떻게해야합니까? 같은 줄의 모든 논리는 필드와 c'tor 사이에 흩어져있는 논리보다 낫습니다.
— Elazar Leibovich 2011 년

@Elazar : 이와 같이 컴파일 타임에 알려진 특정 값으로 필드를 초기화하려면 일반적으로 해당 필드를 변경할 수 없으므로 ImmutableSet. 정말로 변경 가능하도록하려면 생성자 또는 인스턴스 이니셜 라이저 블록 또는 정적 필드 인 경우 정적 이니셜 라이저 블록에서 초기화 할 수 있습니다.
— ColinD 2011 년

1
어, ImmutableMap분명히 거기에 말 했어야 했어.
— ColinD 2011 년

나는 그렇게 생각하지 않는다. 차라리 동일한 정의 줄에서 초기화를보고, 비 정적 초기화 {{init();}}(다른 생성자가 잊어 버릴 수 있으므로 생성자가 아닌)에 넣는 것이 좋습니다. 그리고 그것은 일종의 원자 적 행동이라는 것이 좋습니다. 지도가 휘발성 인 경우 빌더로 초기화하면 항상 null절반이 채워지지 않고 항상 최종 상태 인지 확인합니다 .
— Elazar Leibovich 2011 년

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

얼마 전 비슷한 요구 사항이있었습니다. 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;
    }
}

그런 다음 필요에 맞는 방법으로 유창한 빌더를 만듭니다.

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

이것은 특히 테스트 픽스처를 설정하는 동안 항상 원했던 것입니다. 마지막으로, 모든지도 구현을 구축 할 수있는 간단한 유창한 빌더를 작성하기로 결정했습니다-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.