코드를 짜다가 foreach문을 중첩으로 사용하는 경우가 생각보다 자주 있다. 이를 더 개선할 방법이 없을까 하다가 이번에 새롭게 알게 된 문법을 소개해보려고 한다.
Function.identity()
Function.identity()는 Java 8에서 도입된 함수형 프로그래밍 인터페이스 중 하나인 Function 인터페이스의 정적 메서드이다. 이 메서드는 입력 값을 그대로 반환하는 함수 객체를 생성하는 데 사용된다. 즉, Function<T, T> 타입의 객체로서, 입력 값을 변경하지 않고 그대로 반환한다.
import java.util.function.Function;
예제: Function.identity() 사용
다음은 Function.identity()를 사용하여 리스트를 맵으로 변환하는 예시 코드이다:
import lombok.*;
import java.util.*;
@AllArgsConstructor
@NoArgsConstructor
@Getter
class ItemOption {
private String optionCode;
private int price;
private int buyPrice;
}
public class Main {
public static void main(String[] args) {
List<ItemOption> itemOptionList = Arrays.asList(
new ItemOption("code1", 100, 50),
new ItemOption("code2", 200, 100),
new ItemOption("code3", 300, 150)
);
// List를 Map으로 변환: key는 optionCode, value는 ItemOption 객체
Map<String, ItemOption> optionMap = itemOptionList.stream()
.collect(Collectors.toMap(ItemOption::getOptionCode, Function.identity()));
// Map 출력
optionMap.forEach((key, value) -> {
System.out.println("Key: " + key + ", Value: " + value.getPrice() + ", " + value.getBuyPrice());
});
}
}
Collectors.toMap과 Function.identity() 활용
Function.identity()는 특히 스트림을 사용할 때 자주 사용된다. 스트림에서의 변환 과정에서 입력 값을 그대로 반환하는 경우, 불필요한 람다식을 사용하는 대신 Function.identity()를 사용할 수 있다. 이는 코드의 가독성을 높이고, 의도를 명확히 전달하는 데 도움이 된다.
위 예제에서 ItemOption::getOptionCode는 ItemOption 객체의 optionCode 필드를 키로 추출하고, Function.identity()는 해당 ItemOption 객체를 그대로 값으로 사용한다. 따라서 Map의 키는 optionCode이고, 값은 ItemOption 객체가 된다.
성능 비교
기존 코드
itemOptionEntityList.forEach(optionEntity -> {
modifyModel.getItemOptionList().forEach(mOpt -> {
if (optionEntity.getOptionCode().equals(mOpt.getOptionCode())) {
optionEntity.changeOptionPrice(mOpt.getPrice(), mOpt.getBuyPrice());
}
});
});
개선된 코드
itemOptionEntityList.forEach(optionEntity -> {
ItemOption modifyOption = modifyOptionMap.get(optionEntity.getOptionCode());
if (Objects.nonNull(modifyOption)) {
optionEntity.changeOptionPrice(modifyOption.getPrice(), modifyOption.getBuyPrice());
}
});
성능 분석
1. 시간 복잡도:
- 기존 코드: O(n * m) (n은 itemOptionEntityList의 크기, m은 modifyModel.getItemOptionList()의 크기)
- 두 리스트를 중첩 반복하면서 비교하므로, 각 옵션 엔티티마다 모든 수정 모델 옵션을 순회해야 한다.
- 개선된 코드: O(n + m) (n은 itemOptionEntityList의 크기, m은 modifyModel.getItemOptionList()의 크기)
- modifyModel.getItemOptionList()를 Map으로 변환하는 작업은 O(m).
- 각 옵션 엔티티를 한 번씩 순회하면서 Map에서 해당 옵션 코드를 키로 조회하는 작업은 O(1) (평균).
- 따라서 전체 시간 복잡도는 O(n + m).
2. 공간 복잡도:
- 개선된 코드에서는 추가적으로 Map을 저장할 공간이 필요하므로, 약간의 공간 복잡도가 증가하지만, 이는 성능 이점에 비해 작은 비용이다.
결론
개선된 코드는 시간 복잡도 측면에서 더 효율적이다. 중첩 반복을 제거하고, 대신 Map을 사용하여 상수 시간 복잡도로 필요한 옵션을 조회할 수 있기 때문이다. 따라서 큰 데이터셋에서도 더 나은 성능을 기대할 수 있다.
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
Java 제네릭의 이해: 제네릭 메서드 (0) | 2024.05.23 |
---|---|
폴링(Polling) 사용해서 CompletableFuture 결과 확인하기 (0) | 2024.02.07 |
CompletableFuture를 활용한 비동기 메일 전송 구현 (1) | 2024.02.06 |
AtomicInteger를 활용한 파일 저장 순서 동기화 문제 해결 (0) | 2024.02.04 |
[JAVA] 코드 최적화를 위한 매직 상수 사용법 (4) | 2023.12.31 |