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값을 지원하지 않습니다 . 그래서이 방법의 제한이 : 당신은 당신의 값을 설정할 수 없습니다 HashMapnull.
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.