나만 몰랐던 Java Map Iteration
2021, Aug 26
Java Map 반복문 알고 쓰기
개요
Intellij 에서 SonarLint로 java 코드를 확인하다 HashMap 반복문을 사용하는 부분에서 다음과 같은 경고를 보게 되었습니다.
“entrySet()” should be iterated when both the key and value are needed
경고 나온 부분 예시
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
...
}
-
루프문에서 맵의 key, value 가 모두 필요할 때 entrySet 으로 반복하라는 경고메시지를 받았습니다.
-
항상 저렇게만 반복해왔었는데.. Map 구현체에 대해 반복문을 사용하는 적절한 방법에 대해 알아보고자 하였습니다. (
사실나만모르는게아닐까?)
Key,Value 를 모두 사용하는 부적절한 반복문
public void doSomethingWithMap(Map<String,Object> map) {
for (String key : map.keySet()) { // Noncompliant; for each key the value is retrieved
Object value = map.get(key);
// ...
}
}
- 제가 위에서 저지른 잘못된 방법입니다.
- map.get() 은 key를 인자로 받아 해당 키로 값을 한번 더 조회하기 때문에 비효율적이고 최악의 경우 O(n) 의 효율을 가진다고 합니다.
- key 또는 value 만 필요할 때만 위와 같이 반복합니다.
Key , Value 를 모두 사용하는 적절한 반복문
public void doSomethingWithMap(Map<String,Object> map) {
for (Map.Entry<String,Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// ...
}
}
- entrySet으로 Map을 돌면 한번에 Key,Value를 모두 참조 가능해 key로 값을 한번 더 조회하는 작업이 사라집니다.
Key 또는 Value만 사용하는 적절한 반복분
public void doSomethingWithMap(Map<String,Integer> map) {
for (Integer values : map.values()) {
// values 사용
}
}
- 오히려 key,value 중 하나만 필요하다면 이 방법이 성능향상에 도움이 됩니다.
성능비교
// map size: 5000000
// key,value 모두 사용하는 잘못된 반복
static void hashLoopNonCompliant(){
for(String keys: hs.keySet()){
res1 += hs.get(keys);
}
}
// key,value 모두 사용하는 옳은 반복
static void hashLoopCompliant(){
for(Map.Entry<String,Integer> entry : hs.entrySet()){
res2 += entry.getValue();
}
}
// value 만 사용하는 반복
static void hashLoopValues(){
for(Integer values : hs.values()){
res3 += values;
}
}
결과
결론
-
key,value 모두 사용하는 반복을 할 때는 Entry entrySet() 을 이용하자! keySet을 불러와 .get(key) 로 값을 다시 찾지 말자!
-
코드분석 플러그인 말을 잘듣자
번외: 컬렉션 타입 선언은 인터페이스로 하자!
by ref
-
Java Collections API의 목적은 세부적인 구현물들을 잘 숨기기 위해 잘 정의된 인터페이스 계층 구조를 제공하는 것이라고 합니다.
-
구현 클래스는 새 컬렉션을 인스턴스화 하는데 사용하고 그 결과는 컬렉션 인터페이스 변수에 할당되어야 합니다.
Noncompliant
private HashMap<Key,Val> hs = new HashMap<>();
Compliant
private Map<Key,Val> hs = new HashMap<>();