이펙티브 37 - EnumMap

참치돌고래·2022년 12월 27일
0

이펙티브 자바

목록 보기
20/21

enumMap 을 사용하자


class Plant {
	enum LifeCycle {ANNUAL, PEREMINAL, BIENNIAL}
    
    final String name;
    final LifeCycle lifeCycle;
    
    Plant(String name, LifeCycle lifeCycle) {
    	this.name = name;
        this.lifeCycle = lifeCycle;
    }
    
    @Override public String toString() {
    	return name;
    }
}

/** 
* 방법 1
**/
Map<Plant.LifeCycle, Set<Plant>> platnsByLifeCycle = 
	new EnumMap<>(Plant.LifeCycle.class);
for (Plant.LifeCycle lc : Plant.LifeCycle.values())
	plantsByLifeCycle.put(lc, new HashSet<>());
for (Plant p : garden)
	plantsByLifeCycle.get(p.lifeCycle).add(p);
System.out.println(plantsByLifeCycle);

/**
* 방법 2 - 1. 
**/
System.out.println(Arrays.stream(garden)
	.collect(groupingBy(p -> p.lifeCycle));
    
/**
* 방법 2 - 2
**/
System.out.println(Arrays.stream(garden)
	.collect(groupingBy(p -> p.lifeCycle,
    		() -> new EnumMap<>(LifeCycle.class), toSet())));

방법 1과 방법 2 - * 의 차이는 방법 1은 모든 enum type 에 대해서 set을 생성한다는 것이고 , 방법 2는 garden에 있는 enum type만을 set을 생성한다는 것이다.

stream을 사용한 방법2는 방법1보다 간결하지만 overhead 가 발생할 수 있다. 하지만 parallel processing을 통해 성능을 끌어낼 수 있다.

EnumMap

EnumMap은 기존 enum 클래스에 있는 enum type만을 키값으로 설정할 수 있기 때문에,
HashTable 보다 comapct 한 배열을 사용한다. 이것은 HashMap보다 더 적은 메모리와 더 좋은 성능을 보여준다

  • Map 성능에 영향을 주는 요소들
  1. Size : Map의 사이즈가 작을수록, 삽입, 조회의 성능이 좋다.
  2. Key Type : Map의 키 역시 성능에 영향을 주는데, String 형태의 key보다 enum 형태의 key가 더 성능이 좋다. enum 키는 가능한 값이 고정되어있어 더 compact한 배열을 사용하여 키와 값들을 저장할 수 있다.
  3. Load factor : load factor가 작을 수록, map 크기의 resizing은 빈번해져 map 성능에 영향을 준다. enumMap은 hashMap보다 loadFactor가 기본적으로 크기때문에 이 역시 영향 요소 중 하나이다.

결과의 차이를 확연하게 주기위해 hashMap의 initial size100으로 주었다. 물론 반복문의 개수와 같은 1000000로 주어도 load factor가 0.75이기때문에 enumMap보다 조금 느리다 평균 3~4milliseconds정도
테스트 코드

		Map<String, Integer> hashMap = new HashMap<>(100, 0.75f);

        Map<Plant.LifeCycle, Integer> enumMap = new EnumMap<>(Plant.LifeCycle.class);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            hashMap.put(String.valueOf(i), i);

        }
        long endTime = System.currentTimeMillis();

        System.out.println("Elapsed time for HashMap: " + (endTime - startTime) + " milliseconds");

        startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            enumMap.put(Plant.LifeCycle.values()[i % 3], i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("Elapsed time for enumMap: " + (endTime - startTime) + " milliseconds");

output

	Elapsed time for HashMap: 22 milliseconds
	Elapsed time for enumMap: 13 milliseconds

EnumMap 의 활용사례

public enum Phase{
        SOLID, LIQUID, GAS;

        public enum Transition {
            MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
            BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID);
            
            private final Phase from;
            private final Phase to;
            
            Transition(Phase from, Phase to) {
                this.from = from;
                this.to = to;
            }
            
            private static final Map<Phase, Map<Phase, Transition>>
             m = Stream.of(values())
                .collect(groupingBy(t -> t.from,
                    () -> new EnumMap<>(Phase.class),
                    toMap(t -> t.to, t -> t,
                        (x, y) -> y, () -> new EnumMap<Phase, Transition>(Phase.class)
                    )
                ));
            
            public static Transition from(Phase from, Phase to) {
                return m.get(from).get(to);
            }
        }
        
    }
profile
안녕하세요

0개의 댓글