해당 상수가 그 열거 타입에서 몇 번째 위치인지를 반환하는 메소드 item 35
장바구니에 담긴 과자들을 타입(봉지과자, 박스과자, 통과자) 별로 분리하는 프로그램을 작성해보자!
public class Snack {
enum Type { BOX, BAG, BARREL }
final String name;
final Type type;
Snack(String name, Type type) {
this.name = name;
this.type = type;
}
@Override
public String toString() {
return "Snack{" +
"name='" + name + '\'' +
'}';
}
}
public class ShoppingBasket {
public static void main(String[] args) {
// 과자의 타입마다 분리하기 위해 Set 배열을 만들고, 초기 사이즈를 과자의 총 타입의 갯수로 지정한다.
Set<Snack>[] snacksByType = (Set<Snack>[]) new Set[Type.values().length];
// 초기화를 해준다.
for (int i = 0 ; i < snacksByType.length ; i++) {
snacksByType[i] = new HashSet<>();
}
// 장바구니에 과자를 담는다.
Snack[] shoppingBasket = {
new Snack("오레오오즈", Type.BOX),
new Snack("포카칩", Type.BAG),
new Snack("바나나킥", Type.BAG),
new Snack("닭다리스낵", Type.BOX),
new Snack("콘칲", Type.BAG)
};
// 장바구니에 담은 과자들을 타입별로 분리한다.
for (Snack snack : shoppingBasket) {
snacksByType[snack.type.ordinal()].add(snack); // 이 때, ordinal()을 사용한다. (BOX = 0, BAG = 1)
}
// 결과를 출력한다.
for (int i = 0; i < snacksByType.length; i++) {
System.out.printf("%s: %s%n", Type.values()[i], snacksByType[i]);
}
}
}
- 배열은 제네릭과 호환되지 않기 때문에 비검사 형변환이 수행되고, 컴파일이 안된다.
- 초기화를 할 때, 정수 값을 잘못 입력하면
ArrayIndexOutOfBoundException
이 발생한다. ordinal()
은 상수 선언 순서에 따라 변한다.
Map 인터페이스에서 키를 특정 열거형 타입만을 사용하도록 하는 구현체
public class ShoppingBasket {
public static void main(String[] args) {
// 과자의 타입마다 분리하기 위해 EnumMa을 사용한다.
Map<Snack.Type, Set<Snack>> snacksByType = new EnumMap<>(Snack.Type.class);
// 초기화를 해준다.
for (Snack.Type type : Snack.Type.values()) {
snacksByType.put(type, new HashSet<>());
}
// 장바구니에 과자를 담는다.
Snack[] shoppingBasket = {
new Snack("오레오오즈", Type.BOX),
new Snack("포카칩", Type.BAG),
new Snack("바나나킥", Type.BAG),
new Snack("닭다리스낵", Type.BOX),
new Snack("콘칲", Type.BAG)
};
// 장바구니에 담은 과자들을 타입별로 분리한다.
for (Snack snack : shoppingBasket) {
snacksByType.get(snack.type).add(snack);
}
// 결과를 출력한다.
System.out.println(snacksByType);
}
}
- 코드가 더 간결해지고 성능도 비슷하다.
- 배열 인덱스를 계산하는 과정에서 오류가 날 일이 없고, 타입에 안전하게 사용할 수 있다.
- 출력용 문자열을 제공해주어, 결과를 확인할 때 따로 formatting을 해주지 않아도 된다.
위 코드를 Stream을 사용해서 조금 더 단순하게 바꿀 수 있다.
public class ShoppingBasket {
public static void main(String[] args) {
// 장바구니에 과자를 담는다.
Snack[] shoppingBasket = {
new Snack("오레오오즈", Type.BOX),
new Snack("포카칩", Type.BAG),
new Snack("바나나킥", Type.BAG),
new Snack("닭다리스낵", Type.BOX),
new Snack("콘칲", Type.BAG)
};
// 장바구니에 담은 과자들을 타입별로 분리한 결과를 출력한다.
System.out.println(Arrays.stream(shoppingBasket)
.collect(groupingBy(s -> s.type)));
}
}
EnumMap을 사용할 때 얻는 공간과 성능 이점이 사라진다는 문제가 있으니 주의해서 사용해야한다.
public class ShoppingBasket {
public static void main(String[] args) {
// 장바구니에 과자를 담는다.
Snack[] shoppingBasket = {
new Snack("오레오오즈", Type.BOX),
new Snack("포카칩", Type.BAG),
new Snack("바나나킥", Type.BAG),
new Snack("닭다리스낵", Type.BOX),
new Snack("콘칲", Type.BAG)
};
// 장바구니에 담은 과자들을 타입별로 분리한 결과를 출력한다.
System.out.println(Arrays.stream(shoppingBasket)
.collect(groupingBy(s -> s.type, () -> new EnumMap<>(Type.class), toSet())));
}
}
EnumMap 버전은 언제나 열거 타입당 하나씩 중첩 맵을 만들지만, Stream에서 사용하면 해당 열거 타입에 속하는 객체가 있을 때만 만든다.
배열의 인덱스를 얻기 위해 ordinal()
을 사용하는 것 보다는 EnumMap
을 사용하는 것이 좋다.