정적지도를 초기화하는 방법


1131

MapJava 에서 정적 을 어떻게 초기화 합니까?

방법 1 : 정적 이니셜 라이저
방법 2 : 인스턴스 이니셜 라이저 (익명 서브 클래스) 또는 다른 방법?

각각의 장단점은 무엇입니까?

다음은 두 가지 방법을 보여주는 예입니다.

import java.util.HashMap;
import java.util.Map;

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}

2
: 자바 (8)지도를 초기화 stackoverflow.com/a/37384773/1216775
akhil_mittal

2
이중 괄호 초기화를 사용하지 마십시오 . 해킹이므로 메모리를 누출하고 다른 문제를 일으키는 쉬운 방법입니다.
dimo414

자바 9? 항목 수를 <= 10을 사용하는 경우 Map.of다른 Map.ofEntries확인 stackoverflow.com/a/37384773/1216775
akhil_mittal

답변:


1106

이 경우 인스턴스 이니셜 라이저는 단지 구문 설탕일까요? 왜 초기화하기 위해 여분의 익명 클래스가 필요한지 모르겠습니다. 그리고 생성되는 클래스가 최종 클래스이면 작동하지 않습니다.

정적 이니셜 라이저를 사용하여 불변 맵을 만들 수도 있습니다.

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

10
이것은 내가 몇 년 동안 사용한 관용구이며, 아무도 그것에 대해 시선을 끄는 적이 없었습니다. 수정 불가능한 상수 세트 및 목록에 대해서도 동일하게 수행합니다.
jasonmp85

3
String 키로 HashMap <String, String>을 어떻게 처리합니까? Map 객체는 String 키를 가질 수 없으므로 unmodifiableMap ()을 사용할 수 없습니다. HashMap으로 캐스팅하면 목적도 무효가 될 것 같습니다. 어떤 아이디어?
Luke

30
@Luke Android에 이러한 제한이 있다고 의심합니다. 전혀 말이되지 않습니다. 빠른 검색 에서이 질문 (및 다른 많은 질문 )이 Android의 Map 객체에 문자열 키를 사용할 수 있음을 의미하는 것으로 나타났습니다 .
mluisbrown

11
따라서 다른 사람이 조사 할 필요가 없으므로 Android에서 Map 객체에 String 키를 사용하는 데 문제가 없음을 확인할 수 있습니다.
Jordan

11
요르단 : 지금은 오래된 주제이지만 @Luke가 다른 키 유형 (예 : Map <Integer, String>)이있는 맵에서 문자열을 키로 사용하려고 한 것 같습니다.
불가능한 변수

445

정적, 불변의 맵을 초기화하는 구아바 방식이 마음에 듭니다 .

static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);

보시다시피,의 편리한 팩토리 메소드 때문에 매우 간결 ImmutableMap합니다.

지도에 5 개 이상의 항목이 있으면 더 이상 사용할 수 없습니다 ImmutableMap.of(). 대신 ImmutableMap.builder()다음 줄을 따라 시도하십시오 .

static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();

Guava의 불변 수집 유틸리티의 이점에 대한 자세한 내용은 Guava 사용 설명서에 설명불변 수집을 참조하십시오 .

구아바의 일부는 Google 컬렉션 이라고 불 렸습니다 . Java 프로젝트에서이 라이브러리를 아직 사용하지 않는 경우 시도해 보는 것이 좋습니다. Guava는 다른 SO 사용자가 동의 한대로 Java 용으로 가장 인기 있고 유용한 무료 타사 라이브러리 중 하나가되었습니다 . (만약 당신이 처음이라면, 그 링크 뒤에는 훌륭한 학습 자료가 있습니다.)


업데이트 (2015) : Java 8의 경우 구아바 접근 방식이 다른 것보다 깨끗하기 때문에 여전히 사용합니다. 구아바 의존성을 원하지 않으면 평범한 오래된 init 메소드를 고려하십시오 . 2 차원 배열과 Stream API 가 있는 해킹 은 나에게 묻는다면 꽤 추악하고 키와 값이 같은 유형이 아닌 Map을 만들어야하는 경우 (질문과 같이) 더 추악 해집니다 Map<Integer, String>.

자바 팔에 관해서 일반적으로 구아바의 미래에 관해서는, 루이 Wasserman은 이 말했다 2014 년 다시하고 [ 갱신 2016]는이 발표 된 구아바 (21)가 필요하고 적절하게 자바 (8) 지원합니다 .


업데이트 (2016) :로 Tagir Valeev 보낸 지적 , 자바 (9) 마지막으로 추가하여, 아무것도하지만, 순수한 JDK를 사용하지 않고 할이 깨끗한 것 편의 팩토리 메소드 모음을 :

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);

21
동료 SO 관리자가 내가 연결 한 훌륭한 "자유로운 무료 타사 Java 라이브러리"질문을 삭제 한 것 같습니다. :( 젠장.
Jonik

2
상수 맵을 초기화하는 가장 좋은 방법입니다. 더 읽기 쉬울뿐만 아니라 Collections.unmodifiableMap 이 기본 맵의 읽기 전용보기를 리턴하므로 (여전히 수정할 수 있음).
crunchdog

11
삭제 된 질문 (10k + 담당자)을 볼 수 있으므로 여기 에 '가장 유용한 무료 타사 Java 라이브러리'사본이 있습니다. 첫 페이지 일 뿐이지 만 적어도 위에서 언급 한 구아바 자료를 찾을 수 있습니다 .
Jonik

2
추가 종속성없이 수행하는 방법을 아는 것이 좋지만이 방법을 선호합니다.
렌치

2
JEP 186은 아직 닫히지 않았으므로 컬렉션 리터럴과 관련된 새로운 기능을 소개 할 수 있습니다
cybersoft

182

나는 사용할 것이다 :

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
  1. 나는 개인적으로 나쁜 스타일이라고 생각하는 익명 클래스를 피하고 피합니다.
  2. 지도 작성을보다 명확하게합니다.
  3. 지도를 수정할 수 없게 만듭니다
  4. MY_MAP이 상수이므로 상수처럼 이름을 지정합니다.

3
순수한 JDK 옵션 (libs 없음) 중에서 맵 정의가 초기화와 명확하게 연결되어 있기 때문에 이것을 가장 좋아합니다. 또한 지속적인 명명에 동의했다.
Jonik

당신이 할 수있는 일은 나에게 결코 발생하지 않았습니다.
romulusnr

181

Java 5는 이보다 간단한 구문을 제공합니다.

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

46
이 기술을 이중 중괄호 초기화라고합니다. stackoverflow.com/questions/1372113/… 특별한 Java 5 구문이 아니며 인스턴스 이니셜 라이저가있는 익명 클래스의 트릭입니다.
Jesper

13
이중 중괄호 초기화에 대한 빠른 질문 :이 작업을 수행 할 때 Eclipse는 누락 된 일련 ID에 대한 경고를 발행합니다. 한편으로, 나는이 특정한 경우에 시리얼 ID가 필요한 이유를 알지 못하지만 다른 한편으로는 보통 경고를 억제하는 것을 좋아하지 않습니다. 이것에 대한 당신의 생각은 무엇입니까?
nbarraille

8
@nbarraille 때문 HashMap implements Serializable입니다. 실제로이 "트릭"을 사용하여 HashMap의 서브 클래스를 작성하므로, 직렬화 가능 클래스를 내재적으로 작성합니다. 그리고이를 위해서는 serialUID를 제공해야합니다.
noone

5
Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes.- 인 IntelliJ
마크 Jeronimus

3
@MarkJeronimus-제안 된 사용 정적 컨텍스트입니다. 아마도 아주 적은 수의 정적으로 정의 된 맵을 다룰 때 성능은 나빠질 수 있지만 눈에 띄지 않습니다. HashMap.equals에 정의 AbstractMap와 작동 모든 것을 여기에 문제가되지 않습니다, 그래서지도의 서브 클래스입니다. 다이아몬드 연산자는 성가시다. 그러나 언급했듯이 이제 해결되었다.
Jules

95

두 번째 방법의 한 가지 장점 Collections.unmodifiableMap()은 나중에 컬렉션을 업데이트하는 것이 아무것도 없도록 보장 할 수 있다는 것입니다.

private static final Map<Integer, String> CONSTANT_MAP = 
    Collections.unmodifiableMap(new HashMap<Integer, String>() {{ 
        put(1, "one");
        put(2, "two");
    }});

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!

3
새 연산자를 정적 {} 블록으로 옮기고 래핑하여 첫 번째 방법으로 쉽게 수행 할 수 없습니까?
Patrick

2
어쨌든 생성자 호출을 정적 초기화로 이동합니다. 다른 것은 이상하게 보입니다.
Tom Hawtin-tackline

2
구체적인 클래스가 아닌 익명 클래스를 사용하면 어떤 성능이 저하 될 수 있습니까?
Kip

62

다음은 Java 8 단선 정적 맵 초기화 프로그램입니다.

private static final Map<String, String> EXTENSION_TO_MIMETYPE =
    Arrays.stream(new String[][] {
        { "txt", "text/plain" }, 
        { "html", "text/html" }, 
        { "js", "application/javascript" },
        { "css", "text/css" },
        { "xml", "application/xml" },
        { "png", "image/png" }, 
        { "gif", "image/gif" }, 
        { "jpg", "image/jpeg" },
        { "jpeg", "image/jpeg" }, 
        { "svg", "image/svg+xml" },
    }).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));

편집 : Map<Integer, String>질문에서와 같이 초기화 하려면 다음과 같은 것이 필요합니다.

static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
        {1, "one"},
        {2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));

편집 (2) : i_am_zero에 의해 더 나은 혼합 유형 가능 버전이 있으며, 이는 new SimpleEntry<>(k, v)호출 스트림을 사용 합니다. 그 답을 확인하십시오 : https://stackoverflow.com/a/37384773/3950982


7
나는 질문과 다른 답변과 동등한 버전을 추가하기 위해 자유를 얻었습니다. 키와 값이 다른 유형의 Map을 초기화하십시오 (그렇지 String[][]않을 것입니다 Object[][]). IMHO,이 접근법은 추악하고 (캐스트와 함께) 더욱 기억하기 어렵다. 직접 사용하지 않을 것입니다.
Jonik

57

Map.of Java 9 이상

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

자세한 내용은 JEP 269 를 참조하십시오. JDK 9는 2017 년 9 월 에 일반 공급 됩니다.


7
또는 10 개 이상의 키-값 쌍을 원하는 경우 다음을 사용할 수 있습니다.Map.ofEntries
ZhekaKozlov

8
당신이 실현 될 때까지이 깨끗하고 모든입니다 그것이 구현하는 방법
중간

너무 슬프다-10 개의 항목 만 지원하는 것처럼 보이며, 그 후에는 Entries를 사용해야합니다. 절름발이.
Somaiah Kumbera

2
JDK의 구현 청결도는 작동하고 계약을 충족하는 한 중요하지 않습니다. 다른 블랙 박스와 마찬가지로, 구현 세부 사항은 실제로 필요한 경우 미래에 항상 수정 될 수 있습니다 ...
vikingsteve

@mid 이것이 Java에서 이것을 수행 할 수있는 유일한 형식 안전 방법입니다.
Luke Hutchison

44

자바 9

우리가 사용할 수있는 Map.ofEntries전화, Map.entry( k , v )각 항목을 만들 수 있습니다.

import static java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
        entry(1, "one"),
        entry(2, "two"),
        entry(3, "three"),
        entry(4, "four"),
        entry(5, "five"),
        entry(6, "six"),
        entry(7, "seven"),
        entry(8, "eight"),
        entry(9, "nine"),
        entry(10, "ten"));

우리는 또한 사용할 수 있습니다 Map.of그의 대답에 Tagir에 의해 제안 여기 하지만 우리가 사용하는 10 개 이상의 항목을 가질 수 없습니다 Map.of.

자바 8 (Neat Solution)

맵 항목 스트림을 만들 수 있습니다. 우리는 이미 두 가지 구현이 Entryjava.util.AbstractMap있는 있습니다 SimpleEntrySimpleImmutableEntry을 . 이 예에서는 전자를 다음과 같이 사용할 수 있습니다.

import java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
            new SimpleEntry<>(1, "one"),
            new SimpleEntry<>(2, "two"),
            new SimpleEntry<>(3, "three"),
            new SimpleEntry<>(4, "four"),
            new SimpleEntry<>(5, "five"),
            new SimpleEntry<>(6, "six"),
            new SimpleEntry<>(7, "seven"),
            new SimpleEntry<>(8, "eight"),
            new SimpleEntry<>(9, "nine"),
            new SimpleEntry<>(10, "ten"))
            .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));

2
new SimpleEntry<>()방법은 정적보다 훨씬 덜 읽을 수있는 put()/ :
Danon

32

Eclipse Collections를 사용 하면 다음이 모두 작동합니다.

import java.util.Map;

import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Maps;

public class StaticMapsTest
{
    private static final Map<Integer, String> MAP =
        Maps.mutable.with(1, "one", 2, "two");

    private static final MutableMap<Integer, String> MUTABLE_MAP =
       Maps.mutable.with(1, "one", 2, "two");


    private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();


    private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
        Maps.mutable.with(1, "one", 2, "two").asSynchronized();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").toImmutable();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
        Maps.immutable.with(1, "one", 2, "two");
}

Eclipse 콜렉션을 사용하여 기본 맵을 정적으로 초기화 할 수도 있습니다.

import org.eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps;

public class StaticPrimitiveMapsTest
{
    private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two");

    private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asUnmodifiable();

    private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asSynchronized();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .toImmutable();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
            IntObjectMaps.immutable.<String>empty()
                    .newWithKeyValue(1, "one")
                    .newWithKeyValue(2, "two");
} 

참고 : 저는 Eclipse Collections의 커미터입니다.


1
Eclipse Collections가 Java의 기본 콜렉션 라이브러리 가기를 바랍니다. 나는 Guava + JCL보다 훨씬 더 즐겁습니다.
Kenny Cason

29

이 상황에서 익명의 하위 클래스를 만들지 않습니다. 예를 들어 맵을 수정 불가능하게하려면 정적 초기화 프로그램이 동일하게 작동합니다.

private static final Map<Integer, String> MY_MAP;
static
{
    Map<Integer, String>tempMap = new HashMap<Integer, String>();
    tempMap.put(1, "one");
    tempMap.put(2, "two");
    MY_MAP = Collections.unmodifiableMap(tempMap);
}

1
어떤 상황에서 익명 서브 클래스를 사용하여 해시 맵을 초기화 하시겠습니까?
dogbane

6
컬렉션을 초기화하지 마십시오.
eljenso

익명의 서브 클래스를 만드는 것보다 정적 이니셜 라이저를 사용하는 것이 더 나은 선택 인 이유를 설명해 주시겠습니까?
leba-lev

3
@rookie 다른 답변에는 정적 초기화를 선호하는 몇 가지 이유가 있습니다. 여기서 목표 초기화하는 것이므로 키를 몇 번만 저장하는 것을 제외하고 왜 서브 클래스를 가져와야합니까? (키 스트로크를 줄이고 싶다면 Java는 프로그래밍 언어로 좋은 선택이 아닙니다.) Java로 프로그래밍 할 때 사용하는 경험 법 중 하나는 서브 클래스를 가능한 한 적게 (합리적으로 피할 수 없을 때) 사용하는 것입니다.
eljenso

@eljenso-내가 일반적으로 서브 클래스 구문을 선호하는 이유는 그것이 속한 곳에 초기화를 인라인 으로 넣기 때문이다 . 두 번째로 가장 좋은 방법은 초기화 된 맵을 반환하는 정적 메서드를 호출하는 것입니다. 그러나 코드를 살펴보고 MY_MAP의 출처를 알아내는 데 몇 초를 소비해야한다는 것을 두려워합니다.이 시간을 낭비하고 싶지 않습니다. 가독성 향상은 보너스이며 성능 결과는 미미하므로 나에게 가장 적합한 옵션 인 것 같습니다.
Jules

18

페이지에있는 비디오 등 Google 컬렉션 을 확인하는 것이 흥미로울 수 있습니다 . 그것들은 맵과 세트를 초기화하는 다양한 방법을 제공하며 불변 컬렉션도 제공합니다.

업데이트 :이 라이브러리의 이름은 이제 Guava 입니다.


17

익명 클래스는 처리하기 쉽기 때문에 좋아합니다.

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});

12
public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

둘 이상의 상수를 선언하면 해당 코드는 정적 블록으로 작성되며 향후 유지 관리가 어렵습니다. 따라서 익명 클래스를 사용하는 것이 좋습니다.

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}

그리고 상수에 대해서는 수정 불가능한 맵을 사용하는 것이 좋습니다. 그렇지 않으면 상수로 취급 할 수 없습니다.


10

정적 블록 스타일보다 "이중 괄호 초기화"스타일을 강력하게 제안 할 수 있습니다.

누군가 익명 클래스, 오버 헤드, 성능 등을 좋아하지 않는다고 언급 할 수 있습니다.

그러나 내가 더 고려하는 것은 코드 가독성과 유지 보수성입니다. 이 관점에서, 이중 괄호는 정적 메소드보다 코드 스타일이 더 좋습니다.

  1. 요소는 중첩되고 인라인됩니다.
  2. 절차 적이 지 않은 OO입니다.
  3. 성능 영향은 실제로 작으며 무시할 수 있습니다.
  4. 더 나은 IDE 개요 지원 (많은 익명 정적 {} 블록)
  5. 관계를 맺기 위해 몇 줄의 주석을 저장했습니다.
  6. 초기화되지 않은 객체의 요소 누수 / 인스턴스 리드가 예외 및 바이트 코드 최적화 프로그램에서 발생하지 않도록합니다.
  7. 정적 블록의 실행 순서에 대해 걱정할 필요가 없습니다.

또한 익명 클래스의 GC를 알고 있으므로를 사용하여 언제든지 일반 HashMap으로 변환 할 수 있습니다 new HashMap(Map map).

다른 문제가 발생할 때까지이 작업을 수행 할 수 있습니다. 그렇게하면 다른 코딩 스타일 (예 : 정적, 팩토리 클래스 없음)을 사용해야합니다.


8

일반적인 apache-commons에는 MapUtils.putAll (Map, Object []) 메소드가 있습니다 .

예를 들어 컬러 맵을 만들려면

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

모든 빌드에 Apache Commons를 포함하므로 불행히도 Arrays.asMap( ... )일반 Java에 메소드 가 없으면 이것이 최선의 해결책이라고 생각합니다. 바퀴를 재창조하는 것은 일반적으로 바보입니다. 매우 작은 단점은 제네릭을 사용하면 확인되지 않은 변환이 필요하다는 것입니다.
마이크 설치류

@mikerodent 4.1 버전은 일반적입니다 : public static <K, V> Map <K, V> putAll (최종 맵 <K, V> 맵, 최종 객체 [] 배열)
agad

Tx ... 예, 4.1을 사용하고 있지만 SuppressWarnings( unchecked )Eclipse와 같은 줄을 Map<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
mike rodent

@mikerodent는 Object [] [] 때문이 아닙니까? 업데이트 된 unswear를 참조하십시오-Eclipse에는 경고가 없습니다.
agad

얼마나 이상한가 ... 내가 갈 때 String[][]"경고"를 얻는다! 그리고 물론 당신 KV같은 클래스 인 경우에만 작동합니다 . Eclipse 설정에서 "확인되지 않은 변환"을 "무시"로 설정하지 않았습니까?
마이크 설치류

7

구아바를 사용하고 싶지 않거나 사용할 수 없거나 ImmutableMap.of()변경 가능 해야하는 경우가 가장 좋습니다 Map.

public static <A> Map<String, A> asMap(Object... keysAndValues) {
    return new LinkedHashMap<String, A>() {{
        for (int i = 0; i < keysAndValues.length - 1; i++) {
            put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
        }
    }};
}

매우 콤팩트하며 스트레이 값 (예 : 값이없는 최종 키)을 무시합니다.

용법:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));

7

수정 불가능한 맵을 원한다면 java 9는 인터페이스에 멋진 팩토리 메소드 of를 추가했습니다 Map. Set, List에도 비슷한 방법이 추가되었습니다.

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");


6

익명 클래스 생성을 피하기 위해 정적 이니셜 라이저를 사용하는 것이 좋습니다 (추가 목적은 없음). 정적 초기화 프로그램으로 초기화하는 팁을 나열합니다. 나열된 모든 솔루션 / 팁은 형식이 안전합니다.

참고 : 이 질문은 맵을 수정할 수 없게 만드는 것에 대해 아무 말도하지 않으므로 생략하고 쉽게 할 수 있음을 알고 있습니다 Collections.unmodifiableMap(map).

첫 번째 팁

첫 번째 팁은지도를 로컬로 참조하고 SHORT 이름을 지정한다는 것입니다.

private static final Map<Integer, String> myMap = new HashMap<>();
static {
    final Map<Integer, String> m = myMap; // Use short name!
    m.put(1, "one"); // Here referencing the local variable which is also faster!
    m.put(2, "two");
    m.put(3, "three");
}

두 번째 팁

두 번째 팁은 항목을 추가하는 도우미 메서드를 만들 수 있다는 것입니다. 다음과 같은 경우이 도우미 메서드를 공개로 만들 수도 있습니다.

private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
    p(1, "one"); // Calling the helper method.
    p(2, "two");
    p(3, "three");
}

private static void p(Integer k, String v) {
    myMap2.put(k, v);
}

여기에 도우미 메소드는에 요소 만 추가 할 수 있기 때문에 재사용 할 수 없습니다 myMap2. 재사용 할 수 있도록지도 자체를 도우미 메소드의 매개 변수로 만들 수 있지만 초기화 코드는 더 짧지 않습니다.

세번째 팁

세 번째 팁은 채우기 기능을 사용하여 재사용 가능한 빌더와 유사한 헬퍼 클래스를 작성할 수 있다는 것입니다. 이것은 실제로 형식이 안전한 간단한 10 줄 도우미 클래스입니다.

public class Test {
    private static final Map<Integer, String> myMap3 = new HashMap<>();
    static {
        new B<>(myMap3)   // Instantiating the helper class with our map
            .p(1, "one")
            .p(2, "two")
            .p(3, "three");
    }
}

class B<K, V> {
    private final Map<K, V> m;

    public B(Map<K, V> m) {
        this.m = m;
    }

    public B<K, V> p(K k, V v) {
        m.put(k, v);
        return this; // Return this for chaining
    }
}

5

만들고있는 익명 클래스가 잘 작동합니다. 그러나 이것은 내부 클래스이므로 주변 클래스 인스턴스에 대한 참조를 포함합니다. 따라서 XStream 을 사용하여 특정 작업을 수행 할 수 없다는 것을 알 수 있습니다 . 매우 이상한 오류가 발생합니다.

당신이 알고있는 한,이 접근법은 괜찮습니다. 모든 종류의 컬렉션을 간결하게 초기화하는 데 대부분의 시간을 사용합니다.

편집 : 이것이 정적 클래스라는 의견에서 올바르게 지적했습니다. 분명히 나는 ​​이것을 자세히 읽지 않았다. 그러나 내 의견 여전히 익명의 내부 클래스에 적용됩니다.


3
이 특별한 경우에는 정적이므로 외부 인스턴스가 없습니다.
Tom Hawtin-tackline

아마도 XStream은 이런 것들을 직렬화하려고하지 않아야합니다 (정적입니다. 왜 정적 변수를 직렬화해야합니까?)
jasonmp85

5

간결하고 비교적 안전한 것을 원한다면 컴파일 타임 유형 검사를 런타임으로 전환하면됩니다.

static final Map<String, Integer> map = MapUtils.unmodifiableMap(
    String.class, Integer.class,
    "cat",  4,
    "dog",  2,
    "frog", 17
);

이 구현에는 오류가 발생해야합니다.

import java.util.HashMap;

public abstract class MapUtils
{
    private MapUtils() { }

    public static <K, V> HashMap<K, V> unmodifiableMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        return Collections.<K, V>unmodifiableMap(makeMap(
            keyClazz,
            valClazz,
            keyValues));
    }

    public static <K, V> HashMap<K, V> makeMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        if (keyValues.length % 2 != 0)
        {
            throw new IllegalArgumentException(
                    "'keyValues' was formatted incorrectly!  "
                  + "(Expected an even length, but found '" + keyValues.length + "')");
        }

        HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);

        for (int i = 0; i < keyValues.length;)
        {
            K key = cast(keyClazz, keyValues[i], i);
            ++i;
            V val = cast(valClazz, keyValues[i], i);
            ++i;
            result.put(key, val);
        }

        return result;
    }

    private static <T> T cast(Class<? extends T> clazz, Object object, int i)
    {
        try
        {
            return clazz.cast(object);
        }
        catch (ClassCastException e)
        {
            String objectName = (i % 2 == 0) ? "Key" : "Value";
            String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
            throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
        }
    }
}

4

Java 8에서는 다음과 같은 패턴을 사용했습니다.

private static final Map<String, Integer> MAP = Stream.of(
    new AbstractMap.SimpleImmutableEntry<>("key1", 1),
    new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

가장 간결하고 약간 원형 교차로가 아니지만

  • 그것은 외부의 아무것도 필요하지 않습니다 java.util
  • 형식이 안전하며 키와 값에 다른 유형을 쉽게 수용 할 수 있습니다.

필요한 경우 toMap지도 공급 업체를 포함한 서명을 사용하여지도 유형을 지정할 수 있습니다.
zrvan


4

당신은 사용할 수 있습니다 StickyMapMapEntry에서 Cactoos :

private static final Map<String, String> MAP = new StickyMap<>(
  new MapEntry<>("name", "Jeffrey"),
  new MapEntry<>("age", "35")
);

4

두 번째 접근 방식 (더블 브레이스 초기화)안티 패턴으로 생각 되므로 첫 번째 접근 방식으로 이동합니다.

정적 맵을 초기화하는 또 다른 쉬운 방법은이 유틸리티 함수를 사용하는 것입니다.

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>(keyValues.length / 2);

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");

참고 : Map.ofJava 9 를 사용할 수 있습니다


3

정적 이니셜 라이저 구문이 마음에 들지 않으며 익명의 서브 클래스에 확신이 없습니다. 일반적으로 정적 초기화 프로그램을 사용하는 모든 단점과 이전 답변에서 언급 한 익명 서브 클래스를 사용하는 모든 단점에 동의합니다. 다른 한편으로-이 게시물에 제시된 전문가로는 충분하지 않습니다. 정적 초기화 방법을 선호합니다.

public class MyClass {
    private static final Map<Integer, String> myMap = prepareMap();

    private static Map<Integer, String> prepareMap() {
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "one");
        hashMap.put(2, "two");

        return hashMap;
    }
}

3

나는 대답에 게시 된 접근법을 보지 못했고 좋아했다.

정적 이니셜 라이저는 어색하기 때문에 사용하는 것을 좋아하지 않으며 익명 인스턴스는 각 인스턴스마다 새 클래스를 생성하기 때문에 싫어합니다.

대신 다음과 같은 초기화를 선호합니다.

map(
    entry("keyA", "val1"),
    entry("keyB", "val2"),
    entry("keyC", "val3")
);

불행히도 이러한 메소드는 표준 Java 라이브러리의 일부가 아니므로 다음 메소드를 정의하는 유틸리티 라이브러리를 작성 (또는 사용)해야합니다.

 public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
 public static <K,V> Map.Entry<K,V> entry(K key, V val)

(메소드 이름 앞에 접두사가 필요하지 않도록 'import static'을 사용할 수 있습니다)

다른 컬렉션 (list, set, sortedSet, sortedMap 등)에 유사한 정적 메소드를 제공하는 것이 유용하다는 것을 알았습니다.

json 객체 초기화만큼 좋지는 않지만 가독성에 관한 한 그 방향으로 나아가는 단계입니다.


3

Java는 맵 리터럴을 지원하지 않으므로 맵 인스턴스는 항상 명시 적으로 인스턴스화하고 채워야합니다.

다행히도 팩토리 메소드를 사용하여 Java에서 맵 리터럴의 동작을 근사화 할 수 있습니다 .

예를 들면 다음과 같습니다.

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

산출:

{a = 1, b = 2, c = 3}

한 번에 요소를 맵에 작성하고 채우는 것보다 훨씬 편리합니다.


2

JEP 269 는 Collections API를위한 편리한 팩토리 메소드를 제공합니다. 이 팩토리 메소드는 현재 Java 버전 (8)이 아니지만 Java 9 릴리스를 위해 계획되었습니다.

를 들어 Map이 두 공장 방법은 다음과 같습니다 ofofEntries. 을 사용하면 of교대 키 / 값 쌍을 전달할 수 있습니다. 예를 들어, Map같은 것을 만들려면 {age: 27, major: cs}:

Map<String, Object> info = Map.of("age", 27, "major", "cs");

현재에 대한 10 개의 오버로드 된 버전이 of있으므로 10 개의 키 / 값 쌍이 포함 된 맵을 만들 수 있습니다. 이 제한이 마음에 들지 않거나 키 / 값을 번갈아 사용하는 경우 ofEntries다음을 사용할 수 있습니다 .

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

모두 ofofEntries불변을 반환 Map당신이 건설 한 후 자신의 요소를 변경할 수 있습니다. JDK 9 Early Access를 사용하여 이러한 기능을 시험해 볼 수 있습니다 .


2

글쎄 ... 나는 열거 형을 좋아한다.)

enum MyEnum {
    ONE   (1, "one"),
    TWO   (2, "two"),
    THREE (3, "three");

    int value;
    String name;

    MyEnum(int value, String name) {
        this.value = value;
        this.name = name;
    }

    static final Map<Integer, String> MAP = Stream.of( values() )
            .collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}

2

나는 답변을 읽었으며 내지도 작성기를 작성하기로 결정했습니다. 자유롭게 복사하여 붙여넣고 즐기십시오.

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

/**
 * A tool for easy creation of a map. Code example:<br/>
 * {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
 * @param <K> key type (inferred by constructor)
 * @param <V> value type (inferred by constructor)
 * @author Vlasec (for http://stackoverflow.com/a/30345279/1977151)
 */
public class MapBuilder <K, V> {
    private Map<K, V> map = new HashMap<>();

    /** Constructor that also enters the first entry. */
    private MapBuilder(K key, V value) {
        and(key, value);
    }

    /** Factory method that creates the builder and enters the first entry. */
    public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
        return new MapBuilder<>(key, value);
    }

    /** Puts the key-value pair to the map and returns itself for method chaining */
    public MapBuilder<K, V> and(K key, V value) {
        map.put(key, value);
        return this;
    }

    /**
     * If no reference to builder is kept and both the key and value types are immutable,
     * the resulting map is immutable.
     * @return contents of MapBuilder as an unmodifiable map.
     */
    public Map<K, V> build() {
        return Collections.unmodifiableMap(map);
    }
}

편집 : 최근에, 나는 공공 정적 메소드를 of꽤 자주 발견 하고 그것을 좋아합니다. 코드에 추가하고 생성자를 비공개로 설정하여 정적 팩토리 메소드 패턴으로 전환했습니다.

EDIT2 : 더 최근에는 of정적 가져 오기를 사용할 때 꽤 나빠 보이기 때문에 더 이상 정적 메소드를 좋아하지 않습니다 . mapOf대신 이름을 바꾸어 정적 가져 오기에 더 적합했습니다.

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