enum Rule { DUCK, GOOSE }
class Player {
private final String nickname;
final Rule rule;
Player(String nickname, Rule job) {
this.nickname = nickname;
this.rule = job;
}
}
public static void main(String[] args){
Set<Player>[] playerByJob = (Set<Player>[]) new Set[Rule.values().length];
List<Player> players = Collections.EMPTY_LIST;
for (int i = 0; i < playerByJob.length; i++) {
playerByJob[i] = new HashSet<>();
}
for (Player player : players) {
playerByJob[player.rule.ordinal()].add(player);
}
}
-
ordinal()
을 이용하여 추가하게 되면문제
가 있다. -
제네릭 배열 :
비검사 형변환
을 수행해야 하며, 컴파일 할때깨끗하게 되지 않는다.
-
배열은
각 인덱스의 의미를 모르므로
직접 레이블을 달아주어야 한다. -
정확한 정숫값을 사용한다는 것을
직접 보증
해주어야 한다.→ 잘못된 값을 사용하게 되면 동작이 이상하거나
ArrayIndexOutOfBoundsException
을 던진다.
열거 타입을 키로 사용
하도록 설계한 아주 빠른 Map 구현체
public static void main(String[] args){
Map<Rule, Set<Player>> playerByJob = new EnumMap<>(Rule.class);
List<Player> players = Collections.EMPTY_LIST;
for (Rule rule : Rule.values()) {
playerByJob.put(rule, new HashSet<>()); // G
}
for (Player player : players) {
playerByJob.get(player.rule).add(player);
}
}
- 안전하지 않은 형변환이 없다.
- 배열 인덱스를 다루지 않아도 된다.
- EnumMap 내부에서 배열을 사용하기 때문에 EnumMap을 사용하면 타입 안전성과 배열의 성능을 모두 얻어낼 수 있다.
new EnumMap<>(Rule.class);
- 한정적 타입 토큰 →
런타임 제네릭 타입 정보
제공
- 한정적 타입 토큰 →
enum Rule { DUCK, GOOSE }
class Player {
private final String nickname;
final Rule rule;
Player(String nickname, Rule job) {
this.nickname = nickname;
this.rule = job;
}
}
Map으로 생성
System.out.println(players.stream()
.collect(groupingBy(p -> p.rule))); // 일반 Map
- 키를 Rule로 사용하고, Rule에 매핑되는 값들로
List 생성
EnumMap으로 생성
System.out.println(
players.stream()
.collect(groupingBy(p -> p.rule,
**() -> new EnumMap<>(Rule.class)**,
toSet()))
);
- 스트림 버전에서는
플레이어가 모두 DUCK
인 경우는Goose에 대응하는 Set이 존재하지 않는다.
enum Level {
LEVEL1, LEVEL2;
public enum LevelChange {
LEVEL_UP_1_TO_2, LEVEL_DOWN_2_TO_1;
private static final LevelChange[][] TRANSITIONS = {
{null, LEVEL_UP_1_TO_2},
{LEVEL_DOWN_2_TO_1, null}
}; // LEVEL1, LEVEL2의 매핑 정보 등록
public static LevelChange from(Level fromLevel, Level toLevel) {
return TRANSITIONS[fromLevel.ordinal()][toLevel.ordinal()];
}
}
}
-
ordinal과 배열 인덱스의 관계를 알 방법이 없음.
- Level이나 Level.LevelChange를 변경하면서 TRANSITIONS 배열을 잘못 변경하게 되면 런타임 오류가 발생할 수도 있다.
EnumMap
을 사용해서 정리하는게 훨씬 낫다.
enum Level { LEVEL1, LEVEL2; public enum LevelChange { LEVEL_UP_1_TO_2(LEVEL1, LEVEL2), LEVEL_DOWN_2_TO_1(LEVEL2, LEVEL1); private final Level from; private final Level to; LevelChange(Level from, Level to) { this.from = from; this.to = to; } private static final Map<Level, Map<Level, LevelChange>> m = Stream.of(values()).collect(groupingBy(t -> t.from, ()-> new EnumMap<>(Level.class), toMap( t -> t.to, t -> t, (x, y) -> y, () -> new EnumMap<>(Level.class) ))); public static LevelChange from(Level fromLevel, Level toLevel) { return m.get(fromLevel).get(toLevel); } } }
- 배열의 인덱스를 얻기 위해 ordinal을 쓰는 것은 일반적으로 좋지 않으므로 EnumMap을 사용하라.
- 다차원 관계는
EnumMap<..., EnumMap<...>>
으로 표현하라. Enum.ordinal
을 웬만해서는 사용하지 말자.
- 다차원 관계는