문자 그대로 HashMap을 직접 초기화하는 방법은 무엇입니까?


1091

다음과 같이 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-collections/javadocs/…
ax를

답변:


1341

모든 버전

단 하나의 항목 만 필요한 경우 :이 있습니다 Collections.singletonMap("key", "value").

Java 버전 9 이상의 경우 :

예, 지금 가능합니다. Java 9에는 맵 작성을 단순화하는 몇 가지 팩토리 메소드가 추가되었습니다.

// 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 : 예, 기능을 사용하려면 기능이 아닌 기능을 사용할 수 없습니다. 근데 왜하고 싶니?
yankee

6
당신은 하나의 항목과지도를 필요로하는 경우에있다 Collections.singletonMap():
skwisgaar

3
이제 안정적인 Java 9가 릴리스되었으므로 Javadoc에 대한이 링크를 선호 합니다 . 그리고 하나 더 적은 의존성 때문에 +1!
Franklin Yu

3
Java 9는 어디에 entry문서화되어 있습니까?
nobar

1029

이것은 한 가지 방법입니다.

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

6
훌륭한 링크. GreencoddsTenthRuleOfProgramming에 대한 링크의 참조 는 가치가 있습니다.
michaelok

19
"of"메소드가 최대 5 쌍으로 제한되어 있으므로 "ImmutableMap.builder.put ("k1 ","v1 "). put ("k2 ","v2 "). build ()"로 추가 할 수 있습니까?
kommradHomer


341

타사 라이브러리를 허용하면 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();



26
또한 구아바에는 ImmutableMap.builder.put ( "k1", "v1"). put ( "k2", "v2"). build ();
Xetius

17
ImmutableMap은 null 값에서는 실패하지만 HashMap은 그렇지 않으므로 HashMap과 동일하지 않습니다.
Gewthen

2
이 문제에 직면 할 수있는 다른 사람들을 돕기 위해. 빌더를 다음과 같이 Map <String, String>으로 설정해야합니다. Map <String, String> test = ImmutableMap. <String, String> builder (). put ( "k1", "v1"). put ( "k2", "v2"). build ();
Thiago

이 멋진 젠스입니다!
gaurav

105

이를 수행하는 직접적인 방법은 없습니다-Java에는 맵 리터럴이 없습니다 (아직 Java 8에 대해 제안 된 것 같습니다).

어떤 사람들은 이것을 좋아합니다 :

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

그러면 인스턴스 이니셜 라이저가 이러한 값을 넣는 HashMap의 익명 서브 클래스가 작성됩니다. (그런 식으로, 맵은 동일한 값의 두 배를 포함 할 수 없으며, 두 번째 풋은 첫 번째 값을 덮어 씁니다. 다음 예제에서는 다른 값을 사용합니다.)

일반적인 방법은 다음과 같습니다 (로컬 변수의 경우).

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

15
그래도 기억해야 할 메모리 의미가 있습니다. blog.jooq.org/2014/12/08/…
Amalgovinus

1
@Amalgovinus 기본적으로 새 하위 클래스를 만들면 형식 인수 HashMap를이 하위 클래스로 하드 코딩합니다 . 실제로 제공 한 경우에만 작동합니다. (새로운 (빈) HashMap에서는 형식 인수가 관련이 없습니다.)
Paŭlo Ebermann

1
그것의 청결과 같은 I,하지만 불필요한 익명 클래스를 생성하고 문제가 여기에 설명이 있습니다 c2.com/cgi/wiki?DoubleBraceInitialization
우다 치니

1
@ hello_its_me : stackoverflow.com/a/6802512/1386911 답변 과 동일하기 때문에 형식이 다릅니다. 이 경우이 확장 형식은 가독성을 위해 컴팩트 형식 외에 추가 값을 갖지 않습니다.
Daniel Hári

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는 Monica

1
이전 Java 버전을 사용하여 언급되지 않았지만 읽을 수있는 Apache Utils가있는 다른 솔루션 : MapUtils.putAll (new HashMap <String, String> (), new Object [] { "My key", "my value", ...
Rolintocour

3

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 개의 키-값 쌍을 취하는 몇 가지 방법이 있습니다. 다음은 두 항목의 예입니다.

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

weekendWorker.toString () : {SUNDAY = 사람 {name = 'Bob'}, SATURDAY = 사람 {name = 'Alice'}}

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … )Map.Entry인터페이스를 구현하는 많은 수의 객체를 가져옵니다 . 자바는 그 인터페이스, 하나 개의 가변, 다른 불변을 구현하는 두 개의 클래스를 번들 : AbstractMap.SimpleEntry, AbstractMap.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 = 사람 {name = 'Bob'}, 화요일 = 사람 {name = 'Bob'}, THURSDAY = 사람 {name = 'Carol'}, FRIDAY = 사람 {name = 'Carol'} , MONDAY = 사람 {name = 'Alice'}}

Map.copyOf

자바 10은 그 방법을 추가했다 Map.copyOf. 기존지도를 전달하고 해당지도의 불변 사본을 다시 가져옵니다.

노트

공지 사항지도의 반복자 순서가 생산 것을 통해이 Map.of되어 있지 보장. 항목은 임의의 순서로되어 있습니다. 설명서는 주문이 변경 될 수 있음을 경고하므로 표시된 순서에 따라 코드를 작성하지 마십시오.

이 모든 것을 참고 Map.of…방법이를 반환 Map으로 지정되지 않은 클래스 . 기본 콘크리트 클래스는 Java 버전마다 다를 수 있습니다. 이 익명 성을 통해 Java는 특정 데이터에 가장 적합한 것이 무엇이든 다양한 구현 중에서 선택할 수 있습니다. 예를 들어, 키가 enum 에서 온 경우 Java EnumMap는 표지 아래를 사용할 수 있습니다 .


1

Map.of두 가지 쉬운 방법으로 자신 만의 방법 (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

자바 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

불행히도 키와 값의 유형이 동일하지 않은 경우 varargs를 사용하는 것은 Object...유형 안전을 완전히 사용 하고 잃어 버릴 정도로 합리적이지 않습니다 . 예를 들어 항상 a을 만들고 싶다면 Map<String, String>물론 toMap(String... args)가능하지만 키와 값을 쉽게 혼합 할 수 있으므로 홀수의 인수가 유효하지 않으므로 매우 예쁘지 않습니다.

체인과 같은 메소드를 가진 HashMap의 하위 클래스를 만들 수 있습니다

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();

그러나 내가 지금 사용한 솔루션은 varargs와 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에서 스트림을 사용할 수 있습니다 (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


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.