Map 초기화를 한 번만 해야 하는 이유
Map을 사용하여 데이터를 매핑할 수 있습니다. 대신 사용하려면 사전에 데이터를 초기화해줘야 합니다.
Map의 초기화를 인스턴스를 생성할 때 마다 한다면 성능 이슈가 발생할 수 있습니다.
어떻게 하면 static map 의 초기화를 한 번만 할지 고민하고 작성한 글입니다.
문제 상황
public class AreaCode {
private static Map<String, String> areaCodeMap = new HashMap<>();
public AreaCode() {
areaCodeMap.put("02", "서울");
areaCodeMap.put("064", "제주도");
System.out.println("Map 인스턴스 생성됐다!!!");
}
public static String getAreaNameByMap(String areaCode) {
return areaCodeMap.get(areaCode);
}
}
Map을 static 변수로 생성했습니다. 생성자로 키, 값을 설정했습니다.
public class AreaCodeUtil {
public static String getAreaName(String areaCode) {
AreaCode areaCodeMap = new AreaCode();
return areaCodeMap.getAreaNameByMap(areaCode);
}
}
유틸 함수로 Map을 사용하는 클래스를 생성한 후, 인스턴스의 메서드를 호출합니다.
예상했던 데로 유틸 메서드를 호출할 때마다 인스턴스가 생성됐습니다.
해결 방법
1. Collections.unmodifiableMap
public class AreaCode {
private static final Map<String, String> areaCodeMap;
static {
Map<String, String> initialMap = new HashMap<>();
initialMap.put("02", "서울");
initialMap.put("064", "제주도");
areaCodeMap = Collections.unmodifiableMap(initialMap);
System.out.println("Map 초기화 했다!!!");
}
}
static 블록을 사용하면 클래스가 로드되고 static 블록의 코드가 순차대로 한 번만 실행됩니다.
1. 사용하려는 Map 객체는 final을 사용하여 불변 객체로 만듭니다.
2. unmodifiableMap 메서드를 사용하여 불변의 Map 객체를 반환합니다.
3. 사용하려는 Map 객체에 반환된 Map 객체로 한 번만 초기화합니다.
적용하면 클래스 사용 시점에 로드하고 한 번만 초기화합니다.
2. Map.of (Java 9 이상)
public class AreaCode {
private static final Map<String, String> areaCodeMap;
static {
areaCodeMap = Map.of(
"02", "서울",
"064", "제주도");
System.out.println("Map 초기화 했다!!!");
}
}
unmodifiableMap 메서드를 사용하면 Map.of로 변경할 것을 권장합니다.
/**
* Returns an unmodifiable map containing two mappings.
* @since 9
*/
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2);
}
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3);
}
메서드를 보면 불변의 map 객체를 생성 후 반환합니다.
그러나 단점이 있습니다. of 메서드의 인수는 한정되어 있으므로 각 인수의 개수에 맞게 오버로딩 메서드가 존재합니다.
static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5,
K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
return new ImmutableCollections.MapN<>(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5,
k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
}
가장 인수가 많은 of 메서드입니다. 10개 이상의 인수를 사용한다면 다른 메서드를 사용해야 합니다.
3. Map.ofEntries (Java 9 이상)
import static java.util.Map.entry;
public class AreaCode {
private static final Map<String, String> areaCodeMap;
static {
areaCodeMap = Map.ofEntries(
entry("064", "제주도"),
entry("02", "서울"),
entry("1064", "제주도"),
entry("102", "서울"),
entry("2064", "제주도"),
entry("202", "서울"),
entry("3064", "제주도"),
entry("302", "서울"),
entry("4064", "제주도"),
entry("402", "서울"),
entry("5064", "제주도"),
entry("502", "서울"))
;
System.out.println("Map 초기화 했다!!!");
}
}
ofEntries 메서드는 of 메서드의 단점인 인수 개수 제한을 보완했습니다.
entry 메서드로 각 데이터 쌍을 entry 타입으로 변환합니다.
static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
if (entries.length == 0) { // implicit null check of entries array
@SuppressWarnings("unchecked")
var map = (Map<K,V>) ImmutableCollections.EMPTY_MAP;
return map;
} else if (entries.length == 1) {
// implicit null check of the array slot
return new ImmutableCollections.Map1<>(entries[0].getKey(),
entries[0].getValue());
} else {
Object[] kva = new Object[entries.length << 1];
int a = 0;
for (Entry<? extends K, ? extends V> entry : entries) {
// implicit null checks of each array slot
kva[a++] = entry.getKey();
kva[a++] = entry.getValue();
}
return new ImmutableCollections.MapN<>(kva);
}
}
ofEntries 메서드의 소스를 보면,
entry 개수와 상관없이 항상 불변의 Map 객체를 반환합니다.
entry가 2개 이상인 경우 루프를 돌려서 모든 값을 설정 후 Map 객체를 반환합니다.
참고 자료
https://stackoverflow.com/questions/507602/how-can-i-initialise-a-static-map
'Java' 카테고리의 다른 글
Java 함수형 인터페이스 (0) | 2024.12.17 |
---|---|
가비지 컬렉터(Garbage Collector) 의 종류 (0) | 2023.11.29 |
소나 큐브 사용법(Sonar Qube) (0) | 2023.11.29 |
Lombok (1) | 2023.11.28 |
Java Optional (0) | 2023.09.29 |