기준
Map<Integer, Integer>처럼 같은 타입이 반복되면 key와 value의 의미가 흐려진다. 정렬 조건이 복잡하면 작은 타입을 만드는 편이 안전하다.
중첩 Map이 헷갈리는 이유
다음 구조는 동작할 수 있지만 읽기 어렵다.
Map<String, Map<Integer, Integer>> music = new HashMap<>();
안쪽 Map<Integer, Integer>에서 첫 번째 Integer와 두 번째 Integer의 의미를 계속 기억해야 한다.
| 표현 | 의미 예시 |
|---|---|
outerKey | 장르 |
innerKey | 고유 번호 |
innerValue | 재생 횟수 |
코드를 읽을 때마다 의미를 번역해야 하므로 동률 조건이나 비교 조건에서 실수하기 쉽다.
문제가 생기는 장면
정렬 조건이 한 개면 버틸 수 있다. 하지만 조건이 두 개 이상이면 헷갈리기 시작한다.
.sorted((o1, o2) -> {
if (!o1.getValue().equals(o2.getValue())) {
return Integer.compare(o2.getValue(), o1.getValue());
}
return Integer.compare(o1.getKey(), o2.getKey());
})
여기서 getKey()가 고유 번호이고, getValue()가 재생 횟수라는 사실을 계속 기억해야 한다.
작은 타입을 만들면 쉬워진다
노래 하나가 고유 번호와 재생 횟수를 가진다면 타입으로 표현한다.
class Song {
int id;
int play;
Song(int id, int play) {
this.id = id;
this.play = play;
}
}
자료구조도 의미가 분명해진다.
Map<String, List<Song>> music = new HashMap<>();
정렬 조건도 읽기 쉬워진다.
songs.sort((s1, s2) -> {
if (s1.play != s2.play) {
return Integer.compare(s2.play, s1.play);
}
return Integer.compare(s1.id, s2.id);
});
언제 타입을 만들까
| 상황 | 권장 |
|---|---|
| 값이 하나뿐이다 | Map<K, V>로 충분 |
| 값이 두 개 이상이고 의미가 다르다 | 작은 클래스 고려 |
| 정렬 기준이 여러 개다 | 작은 클래스가 유리 |
| key와 value 타입이 같아서 헷갈린다 | 작은 클래스가 유리 |
| 이미 거의 다 구현했고 수정이 작다 | 현재 구조 유지 가능 |
코딩테스트에서의 현실 기준
처음부터 완벽한 구조를 잡기 어렵다. 이미 중첩 Map으로 많이 작성했다면 끝까지 유지하고, key와 value 의미를 주석으로 고정하는 것도 괜찮다.
다만 다음 문제부터는 "값 하나가 여러 속성을 갖는다"는 느낌이 들면 작은 타입을 먼저 떠올린다.
실수 체크
-
Map<Integer, Integer>의 key와 value 의미가 바로 보이는가 - 정렬 조건에서 key와 value를 반대로 쓰지 않았는가
- 동률 조건이 많다면 작은 클래스를 만들 수 있는가
- 주석 없이도 코드가 읽히는가