본문 바로가기
Java

자바 컬렉션프레임워크 HashMap, HashTable 순서, 중복 알아보기

by 오늘이 내가 된다. 2022. 1. 12.

자바 컬렉션 프레임워크(List, Set, Map)

이전 내용은 위 링크를 참고해주세요


Map

Map인터페이스를 구현. 데이터를 키와 값의 쌍으로 저장

 

- 순서는 존재하지 않는다.

- 중복여부( key : x / value : o )

=> ID는 중복할 수 없지만, password는 중복이 된다고 생각

TreeMap (TreeSet과 같은 특성을 지님)

- 범위 검색과 정렬에 유리한 컬렉션 클래스

- HashMap보다 데이터 추가, 삭제에 시간이 더 소모

 

HashMap (TreeSet과 같은 특성을 지님)

- Map 인터페이스를 구현한 대표적인 컬렉션 클래스

- Map 인터페이스는 순서를 유지하지 않지만, LinkedHashMap클래스를 사용하면 순서 유지가 가능

 

HashMap의 키와 값 (key & value)

- 해싱 기법으로 데이터를 저장. 데이터가 많아도 검색이 빠르다.

- Map 인터페이스를 구현. 데이터를 키와 값의 쌍으로 저장

 

 

출처 : http://www.codechobo.com

key와 value를 하나의 Entry로 묶어서 관리하는 것이 좀 더 객체지향적인 코드라고 할 수 있다.

 

 

해싱

해시함수: 함수를 사용해서 해시테이블의 데이터를 저장 & 읽어오는 것을 말한다.

같은 key를 넣으면 항상 같은 값을 가져와야 한다.

출처 :  http://www.codechobo.com

  1. 키(key)로 해시함수를 호출해서 해시코드(배열의 인덱스)를 얻는다.
  2. 해시코드(해시함수의 반환값)에 대응하는 LinkedList를 배열에서 찾는다.
  3. LinkedList에서 키와 일치하는 데이터를 찾는다.

해시함수는 같은 키에 대해 항상 같은 해시코드를 반환해야 한다.

서로 다른 키일지라도 같은 값의 해시코드를 반환할 수도 있다.

 

 

HashMap 예제 풀어보기

package study.hashmap;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class HashMapEx2 {

	public static void main(String[] args) {
		HashMap map = new HashMap();
		map.put("김자바", new Integer(100)); // put : 생성
		map.put("이자바", new Integer(100));
		map.put("강자바", new Integer(80));
		map.put("안자바", new Integer(90));

		Set set = map.entrySet(); // entrySet: map의 key와 value정보를 저장
		Iterator it = set.iterator(); // iterator : 임의의 순서대로 정렬 (임의대로 줄을 세움)

		while (it.hasNext()) { // hasNext: 배열에 값이 있으면 true 리턴
			Map.Entry e = (Map.Entry) it.next(); // next: 다음 배열원소를 할당 (hasNext와 한 쌍)
			System.out.println("이름 : " + e.getKey() + ", 점수 : " + e.getValue());
		}

		set = map.keySet(); // keySet : key와 value 값 중 key값만 할당
		System.out.println("참가자 명단 : " + set);

		Collection values = map.values(); // values : key와 value 값 중 value값만 할당
		it = values.iterator();

		int total = 0;

		while (it.hasNext()) {
			Integer i = (Integer) it.next();
			total += i.intValue();
		}
		System.out.println("총점 : " + total);
		System.out.println("평균 : " + (float) total / set.size()); // Map의 사이즈 (현재 4명)
		System.out.println("최고점수 : " + Collections.max(values)); // 최대값
		System.out.println("최저점수 : " + Collections.min(values)); // 최소값

	}

}
더보기

Console 출력

이름 : 안자바, 점수 : 90
이름 : 김자바, 점수 : 100
이름 : 강자바, 점수 : 80
이름 : 이자바, 점수 : 100
참가자 명단 : [안자바, 김자바, 강자바, 이자바]
총점 : 370
평균 : 92.5
최고점수 : 100
최저점수 : 80

코드에 해당하는 주석을 처리했으니 코드를 보면 이해를 할 수 있을 것이다.

여기서 중요한 것은 앞서 말했듯이 Map의 순서는 존재하지 않는다는 것이다.

(LinkedHashMap을 사용하면 순서를 가질 수 있다.)

 

 

위 코드의 입력과 출력을 살펴보자.

입력 출력
map.put("김자바", new Integer(100)); 이름 : 안자바, 점수 : 90
map.put("이자바", new Integer(100)); 이름 : 김자바, 점수 : 100
map.put("강자바", new Integer(80)); 이름 : 강자바, 점수 : 80
map.put("안자바", new Integer(90)); 이름 : 이자바, 점수 : 100
더보기

출력코드

	Set set = map.entrySet(); // entrySet: map의 key와 value정보를 저장
		Iterator it = set.iterator(); // iterator : 임의의 순서대로 정렬 (임의대로 줄을 세움)

		while (it.hasNext()) { // hasNext: 배열에 값이 있으면 true 리턴
			Map.Entry e = (Map.Entry) it.next(); // next: 다음 배열원소를 할당 (hasNext와 한 쌍)
			System.out.println("이름 : " + e.getKey() + ", 점수 : " + e.getValue());
		}

Iterator의 사용으로 임의대로 순서를 매겨서 출력을 하였다.

 

그렇다면 다시 출력을 하면 어떻게 될까?

같은 코드를 다시 컴파일하면 '임의의 순서'대로 출력의 순서가 뒤바뀌어야 한다고 생각할 수 있지만, 출력은 동일하다.

그렇다면 순서가 무작위가 아니라 어떤 일정한 규칙을 지닌 것이 아닐까?

 

 

위의 해싱 을 설명할 때 이렇게 말했다.

  1. 키(key)로 해시함수를 호출해서 해시코드(배열의 인덱스)를 얻는다.
  2. 해시코드(해시함수의 반환값)에 대응하는 LinkedList를 배열에서 찾는다.
  3. LinkedList에서 키와 일치하는 데이터를 찾는다.

 

해시코드를 얻었기 때문에 해당 해시코드를 가지고 해당 해시코드에 대응하는 배열을 찾는 것이다.

따라서 Map의 경우 순서는 존재하지 않지만 각각에 해당하는 해시코드는 지정되어 있기 때문에,

컴파일을 다시 하더라도 동일한 해시코드가 지정되어 있어서 배열의 순서(즉, 출력의 순서)가 같을 수 있었다.

 

 

 

이해를 돕기 위해 아래 코드를 보자

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Test3 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
    	HashMap map = new HashMap();
		map.put("김자바", new Integer(100));
		map.put("이자바", new Integer(100));
		map.put("강자바", new Integer(80));
		map.put("안자바", new Integer(90));

		Set set = map.entrySet(); // entrySet: map의 key와 value정보를 저장
		Iterator it = set.iterator(); // iterator : 임의의 순서대로 정렬 (임의대로 줄을 세움)

		while (it.hasNext()) { // hasNext: 배열에 값이 있으면 true 리턴
			Map.Entry e = (Map.Entry) it.next(); // next: 다음 배열원소를 할당 (hasNext와 한 쌍)
			System.out.println("이름 : " + e.getKey() + ", 점수 : " + e.getValue());
		}
		
        Test3 t = new Test3();
        
        System.out.println("김자바 " + t.hashCode("김자바", new Integer(100)));
        System.out.println("이자바 " + t.hashCode("이자바", new Integer(100)));
        System.out.println("강자바 " + t.hashCode("강자바", new Integer(80)));
        System.out.println("안자바 " + t.hashCode("안자바", new Integer(90)));

    }
        
    public int hashCode(String key, Integer value) {  
        return (key == null   ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
    }

}
더보기

Console 출력

이름 : 안자바, 점수 : 90
이름 : 김자바, 점수 : 100
이름 : 강자바, 점수 : 80
이름 : 이자바, 점수 : 100
김자바 44500128
이자바 50700444
강자바 43966729
안자바 50166166

따라서 해당 해시코드(배열의 인덱스) 값은 항상 일정하고,

배열의 인덱스가 작다고 먼저 출력되는 것이 아님을 알 수 있었다.

댓글