본문 바로가기
  • 오늘도 한걸음
카테고리 없음

[JAVA] Map에서 key의 순서가 있을까?

by 우쵸 2021. 12. 3.

 

 ✔ 참고출처 : https://surhommejk.tistory.com/223
 ✔ 참고출처 : https://brocess.tistory.com/210
 ✔ 개인적으로 공부하면서 정리해나가고 있습니다. 맞지 않은 내용이 있을 수 있습니다.
 ✔ 맞지 않는 내용을 계속 고쳐나갈 수 있도록 하겠습니다.

 

코딩 문제 풀이를 하다보면 Map, HashMap을 많이 사용하게 되는데

이상한 현상을 목격했다.

 

1) Map에 put 하는 순서와 다르게 출력된다

2) key값에 Interger 형이 오면 숫자의 순서대로 출력된다

 

Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("사과", 10);
map1.put("딸기", 20);
map1.put("포도", 30);
System.out.println("map1 = " + map1);

Map<Integer, Integer> map2 = new HashMap<Integer, Integer>();        
map2.put(3, 10);
map2.put(1, 20);
map2.put(2, 30);
System.out.println("map2 = " + map2);

 

HashMap 2가지를 만들고 입력한 순서대로 출력되는지 확인해 보았다

결과는 다음과 같다

map1 = {포도=30, 사과=10, 딸기=20}
map2 = {1=20, 2=30, 3=10}

 

Map에는 순서가 없어서 입력(put)한 순서대로 출력된다고 생각했었는데 기대와 달랐다.

어떤 기준이 있는 것일까?

 

Map put 메소드를 보면 해시값을 가지고 입력값을 저장하는 것을 알 수 있다.

//interface Map #put
public V put(K key, V value) {
	return putVal(hash(key), key, value, false, true);
}

 

위에서 작성했던 map1의 해시코드를 출력해보면 다음과 같다.

Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("사과", 10);
map1.put("딸기", 20);
map1.put("포도", 30);
System.out.println("hashCode = " + map1.hashCode());
//hashCode = 4783884

 

여기서 또 궁금한 점은

각각의 key의 출력되는 순서가 hashcode와 무슨 연관이 있는지이다.

 

각 key마다 hashcode 값이 있어서 그 순서대로 출력되는건지 궁금하다

아래 api에서 Map의 해시코드는 entryset의 각 entry들의 해시코드 합으로 정의된다고 나와있다.

즉, 이말은 각 entry가 해시코드 값을 가지고 있다는 의미 아닐까? 

 

int hashCode()

Returns the hash code value for this map. The hash code of a map is defined 
to be the sum of the hash codes of each entry in the map's entrySet() view. 
This ensures that m1.equals(m2) implies that m1.hashCode()==m2.hashCode() 
for any two maps m1 and m2, as required by the general contract of Object.hashCode().

[출처] https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Map.html#hashCode() 

 

Map (Java SE 11 & JDK 11 )

If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null. If the mapping function returns null, no mapping is recorded. If the

docs.oracle.com

 

 

EntrySet에서 각 entry를 뽑아서 해시코드를 출력해 보았다.

 Map<String, Integer> map1 = new HashMap<String, Integer>();
 map1.put("사과", 10);
 System.out.println("key:사과 hashCode = " + map1.hashCode());
 map1.put("딸기", 20);
 System.out.println("key:딸기 hashCode = " + map1.hashCode());
 map1.put("포도", 30);
 System.out.println("key:포도 hashCode = " + map1.hashCode());
 System.out.println("map1 = " + map1);

for (Map.Entry<String, Integer> entry : map1.entrySet()) {
System.out.println("entry.hashCode() = " + entry.hashCode());
}

 

결과는 

hashCode = 1573338
hashCode = 3056070
hashCode = 4783884
map1 = {포도=30, 사과=10, 딸기=20}
포도 entry.hashCode() = 1727814
사과 entry.hashCode() = 1573338
딸기 entry.hashCode() = 1482732

 

각 entry에 저장된 해시코드 값이 출력되었다.

결국 map1을 println으로 출력할 때 뿐만아니라, 확장 for문으로 접근할 때에도 각 entry의 해시코드값에 접근하여 그 순서대로 출력하는 것을 알 수 있었다.

또, api 에 적혀있던 합계 entry의 해시코드값의 합계가 entrySet의 해시코드값과 같다는 것도 확인할 수 있었다.

 

1727814+1573338+1482732 = 4783884

 

그런데 여기서 또 이상한 점 발견

Map<String, Integer> 은 해시코드값이 큰 순서대로 접근을 했는데, Map<Integer, Integer> 에서는 key의 수가 작은 수부터 접근했다는 것이다. 

그렇다면 Integer 타입의 해시코드값은 작은 수일수록 큰 것일까?

 

Map<Integer, Integer> map2 = new HashMap<Integer, Integer>();
map2.put(3, 10);
map2.put(1, 20);
map2.put(2, 30);
map2.put(-1, 40);
map2.put(0, 50);
System.out.println("map2 = " + map2);

for (Map.Entry<Integer, Integer> entry : map2.entrySet()) {
System.out.println(entry.getKey() + " entry.hashCode() = " + entry.hashCode());

결과는

map2 = {-1=40, 0=50, 1=20, 2=30, 3=10}
-1 entry.hashCode() = -41
0 entry.hashCode() = 50
1 entry.hashCode() = 21
2 entry.hashCode() = 28
3 entry.hashCode() = 9

 

Integer 형 Map의 출력순서는 해시코드값의 크기와는 상관이 없는 것 같다.

Integer 형 key의 그 숫자의 순서대로(오름차순) 출력 되는 것으로 보인다.

Map<integer, Integer> 형태 뿐만아니라  Map<Integer, String> 형태에도 key의 그 숫자 순서대로 출력된다.

이 부분은 api 나 다른 자료를 더 참고해보아야 할 것 같다.

 

 

 

결론

1. Map에 데이터를 저장(put)할 때 해시코드값을 같이 저장한다.

2. HashMap은 순서가 보장되지는 않지만 접근시(출력시) 저장된 해시코드값에 영향을 받는다.

3. HashMap의 key가 String형이면 각 entry의 해시코드값이 큰 순서대로 출력되고, key가 Integer 형이면 key의 수 크기 순서대로(오름차순) 출력된다.

4. 어떤 데이터를 key가 Integer인 Map에 모을 경우, 따로 정렬하지 않아도 key의 오름차순으로 출력된다(?)

 

추가

* 순서가 보장되는 LinkedHashMap과 어떻게 다를까?

댓글