728x90
Java에서 두 Map 객체의 공통 키에 해당하는 값을 복사하는 작업은 데이터 처리에서 자주 필요한 기능입니다. 특히, Map 안에 또 다른 Map이 내포되어 있을 경우, 이를 올바르게 복사하려면 깊은 복사를 구현해야 합니다. 이번 포스트에서는 두 Map의 공통 키와 값을 복사하되, 내포된 Map도 재귀적으로 처리하는 방법을 예제와 함께 블로그 형식으로 설명하겠습니다. TypeScript와 비슷한 요구사항을 Java로 구현하는 과정을 단계별로 살펴보겠습니다.
1. 문제 정의
우리의 목표는 다음과 같습니다:
-
두 Map 객체(source와 target)가 있을 때, 공통 키에 해당하는 값만 복사.
-
값이 또 다른 Map인 경우, 해당 Map의 공통 키와 값을 깊게 복사.
-
Java의 타입 안정성을 유지하면서 동적으로 처리.
예를 들어, 다음과 같은 두 Map이 있다고 가정해 봅시다:
Map<String, Object> source = new HashMap<>();
source.put("name", "John");
source.put("age", 30);
Map<String, String> sourceAddress = new HashMap<>();
sourceAddress.put("city", "Seoul");
sourceAddress.put("country", "Korea");
sourceAddress.put("zip", "12345");
source.put("address", sourceAddress);
source.put("hobby", "reading");
Map<String, Object> target = new HashMap<>();
target.put("name", "");
target.put("age", 0);
Map<String, String> targetAddress = new HashMap<>();
targetAddress.put("city", "");
targetAddress.put("country", "");
targetAddress.put("zip", "");
target.put("address", targetAddress);
target.put("job", "");
여기서 공통 키는 name, age, address입니다. 특히 address는 내포된 Map이므로, 이 Map의 공통 키(city, country)만 복사해야 합니다.
2. 해결 방법
Java에서는 Map의 공통 키를 찾아 값을 복사하는 함수를 작성하고, 내포된 Map을 처리하기 위해 재귀를 사용합니다. Java는 TypeScript처럼 동적 타입을 기본적으로 지원하지 않으므로, Object 타입을 사용하거나 제네릭스를 활용해 타입 안정성을 유지합니다.
2.1. 기본 접근법
먼저, 동적으로 키와 값을 처리할 수 있도록 Map<String, Object>를 사용합니다. 값이 Map인지 확인하고, Map이라면 재귀적으로 복사를 수행합니다.
2.2. 공통 요소 복사 함수
다음은 공통 키를 찾아 값을 복사하는 메서드입니다:
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class MapUtils {
public static Map<String, Object> copyMatchingProperties(Map<String, Object> source, Map<String, Object> target) {
Map<String, Object> result = new HashMap<>();
// source와 target의 공통 키 찾기
Set<String> commonKeys = source.keySet().stream()
.filter(target::containsKey)
.collect(Collectors.toSet());
for (String key : commonKeys) {
Object sourceValue = source.get(key);
Object targetValue = target.get(key);
// 값이 Map이고 null이 아닌 경우, 재귀적으로 복사
if (sourceValue instanceof Map && targetValue instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> nestedSource = (Map<String, Object>) sourceValue;
@SuppressWarnings("unchecked")
Map<String, Object> nestedTarget = (Map<String, Object>) targetValue;
result.put(key, copyMatchingProperties(nestedSource, nestedTarget));
} else {
// Map이 아닌 경우, source의 값을 복사
result.put(key, sourceValue);
}
}
return result;
}
}
2.3. 함수 동작 원리
-
공통 키 찾기: source.keySet().stream().filter(target::containsKey)를 사용해 두 Map에 모두 존재하는 키를 필터링합니다.
-
값 처리:
-
값이 Map 타입인 경우, 해당 값을 Map<String, Object>로 캐스팅한 뒤 재귀적으로 copyMatchingProperties를 호출.
-
Map이 아닌 경우(예: 문자열, 숫자 등), source의 값을 그대로 복사.
-
-
결과 반환: 공통 키와 그 값(또는 재귀적으로 복사된 Map)만 포함된 새로운 Map을 반환.
@SuppressWarnings("unchecked")는 제네릭 캐스팅 경고를 억제하기 위해 사용되었습니다. Java의 런타임 타입 소거로 인해 Object를 Map으로 캐스팅할 때 경고가 발생하기 때문입니다.
3. 예제 사용
이제 위 메서드를 실제 데이터에 적용해 보겠습니다:
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
// Source Map
Map<String, Object> source = new HashMap<>();
source.put("name", "John");
source.put("age", 30);
Map<String, String> sourceAddress = new HashMap<>();
sourceAddress.put("city", "Seoul");
sourceAddress.put("country", "Korea");
sourceAddress.put("zip", "12345");
source.put("address", sourceAddress);
source.put("hobby", "reading");
// Target Map
Map<String, Object> target = new HashMap<>();
target.put("name", "");
target.put("age", 0);
Map<String, String> targetAddress = new HashMap<>();
targetAddress.put("city", "");
targetAddress.put("country", "");
targetAddress.put("zip", "");
target.put("address", targetAddress);
target.put("job", "");
// 공통 요소 복사
Map<String, Object> result = MapUtils.copyMatchingProperties(source, target);
// 결과 출력
System.out.println(result);
}
}
출력 결과
{name=John, age=30, address={city=Seoul, country=Korea}}
결과 분석
-
공통 키: name, age, address.
-
address 내포된 Map의 공통 키: city, country.
-
source의 zip은 target의 address에 없으므로 복사되지 않음.
-
target의 job은 source에 없으므로 결과에 포함되지 않음.
4. 제네릭스를 사용한 타입 안정성 강화
위 예제는 Map<String, Object>를 사용해 동적으로 처리했지만, 타입 안정성을 높이기 위해 제네릭스를 활용할 수 있습니다. 예를 들어, Map의 값 타입을 더 구체적으로 제한하고 싶다면 다음과 같이 구현할 수 있습니다:
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class TypedMapUtils {
// Address용 Map 타입 정의
public static class Address extends HashMap<String, String> {}
// Source와 Target용 Map 타입 정의
public static class Source extends HashMap<String, Object> {}
public static class Target extends HashMap<String, Object> {}
public static Map<String, Object> copyMatchingProperties(Source source, Target target) {
Map<String, Object> result = new HashMap<>();
// 공통 키 찾기
Set<String> commonKeys = source.keySet().stream()
.filter(target::containsKey)
.collect(Collectors.toSet());
for (String key : commonKeys) {
Object sourceValue = source.get(key);
Object targetValue = target.get(key);
// Address 타입의 Map 처리
if ("address".equals(key) && sourceValue instanceof Map && targetValue instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, String> nestedSource = (Map<String, String>) sourceValue;
@SuppressWarnings("unchecked")
Map<String, String> nestedTarget = (Map<String, String>) targetValue;
Map<String, String> nestedResult = new HashMap<>();
// 내포된 Map의 공통 키 복사
Set<String> nestedCommonKeys = nestedSource.keySet().stream()
.filter(nestedTarget::containsKey)
.collect(Collectors.toSet());
for (String nestedKey : nestedCommonKeys) {
nestedResult.put(nestedKey, nestedSource.get(nestedKey));
}
result.put(key, nestedResult);
} else {
result.put(key, sourceValue);
}
}
return result;
}
}
이 버전은 address 키의 값이 Map<String, String>임을 명시적으로 처리하며, Source와 Target을 커스텀 클래스로 정의해 타입 안정성을 강화했습니다. 하지만 Java의 제네릭스는 여전히 런타임에서 소거되므로, 캐스팅이 필요할 수 있습니다.
5. 주의사항
-
깊은 복사: 이 메서드는 공통 키에 대해 깊은 복사를 수행합니다. 하지만 배열, 리스트, 또는 커스텀 객체 등 다른 복잡한 타입은 별도로 처리해야 할 수 있습니다.
-
성능: 재귀 호출은 Map이 깊게 중첩된 경우 성능에 영향을 줄 수 있으므로, 대규모 데이터에는 주의가 필요합니다.
-
타입 안정성: Object를 사용하면 유연하지만 타입 오류 위험이 있습니다. 가능하면 제네릭스나 커스텀 클래스를 활용하세요.
-
null 처리: 예제에서는 null 값을 간단히 무시했지만, 실제 프로젝트에서는 null 체크를 더 철저히 해야 할 수 있습니다.
6. 결론
Java에서 두 Map의 공통 요소를 복사하는 작업은 TypeScript와 비슷한 논리를 따르지만, Java의 강한 타입 시스템과 제네릭스 특성상 약간 더 복잡할 수 있습니다. 이 포스트에서 제공한 메서드는 공통 키를 찾아 값을 복사하며, 내포된 Map도 재귀적으로 처리합니다. 동적 처리를 위해 Map<String, Object>를 사용하거나, 타입 안정성을 위해 제네릭스와 커스텀 클래스를 활용할 수 있습니다.
728x90
'Java를 배워보자' 카테고리의 다른 글
JSON Schema: 자바 코드로 Validator 구현하기 (0) | 2024.11.19 |
---|---|
오피넷 API를 활용한 Java 예제: 자세한 가이드 및 실제 코드 구현 (1) | 2024.11.17 |
이클립스에서 Maven 빌드 시 JAR 파일에 의존성 포함하기: Assembly 플러그인 활용 가이드 (0) | 2024.11.16 |
WebDriverManager를 활용한 Selenium WebDriver 자동화 환경 구축 가이드 (0) | 2024.11.16 |
[전체소스]이클립스에서 Maven으로 Selenium 자동화 프로젝트 구성 및 실행하기 (0) | 2024.11.15 |