Computer Science/DataStructure

HashMap과 Hashtable

자바의정석 3rd Edition 2권을 참조하였습니다.

https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=76083001 

 

Java의 정석

저자는 자바를 소개하는데 그치지 않고 프로그래머로써 꼭 알아야하는 내용들을 체계적으로 정리하였으며 200페이지에 달하는 지면을 객체지향개념에 할애함으로써 이 책 한 권이면 객체지향

www.aladin.co.kr

 

Hashtable과 HashMap의 관계는 Vector와 ArrayLIst의 관계와 같아서

Hashtable보다는 새로운 버전인 HashMap을 사용할 것을 권한다.

HashMap은 Map을 구현했으므로 Map의 특징 키(key)와 값(value)을 묶어 하나의 데이터(entry)로 저장한다는 특징을 갖는다. 그리고 해싱(hashing)을 사용하기 때문에 많은 양의 데이터를 검색하는데 있어서 뛰어난 성능을 보인다.

 

HashMap이 데이터를 어떻게 저장하는지 확인하기 위해 실제소스의 일부를 발췌하였다.

더보기
package kr.co.dong.datastructure.hashMap_hashTable;

import java.io.Serializable;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Set;

public class HashMap extends AbstractMap implements Map, Cloneable, Serializable {

	transient Entry[] table;
	// ...
	static class Entry implements Map.Entry {
		final Object key;
		Object value;
        //...
	}
}

HashMap은 Entry라는 내부 클래스를 정의하고, 다시 Entry타입의 배열을 선언하고 있다. 키(key)와 값(value)은 별개의 값이 아니라 서로 관련된 값이기 때문에 각각의 배열로 선언하기 보다는 하나의 클래스로 정의해서 하나의 배열로 다루는 것이 데이터 무결성(integrity)적인 측면에서 더 바람직하다.

비 객체지향적인 코드 객체지향적인 코드
Object[] key;
Object[] value;
Entry[] table;
class Entry {
    Object key;
    Object value;
}

|참고| Map.Entry는 Map인터페이스에 정의된 'static inner interface'이다.

HashMap은 키와 값을 각각 Object타입으로 저장한다. 즉 (Object, Object)의 형태로 저장하기 때문에 어떠한 객체도 저장할 수 있지만 키는 주로 String을 대문자 또는 소문자로 통일해서 사용하곤 한다.

 

키(key)    : 컬렉션 내의 키(key) 중에서 유일해야 한다.

값(value) : 키(key)와 달리 데이터의 중복을 허용한다.

 

키는 저장된 값을 찾는데 사용되는 것이기 때문에 컬렉션 내에서 유일(unique)해야 한다.

즉, HashMap에 저장된 데이터를 하나의 키로 검색했을 때 결과가 단 하나이어야 함을 뜻한다.

만일 하나의 키에 대해 여러 검색결과 값을 얻는다면 원하는 값이 어떤 것인지 알 수 없기 때문이다.

 

예를 들어 사용자ID가 키(key)로, 비밀번호가 값(value)으로 연결되어 저장된 데이터집합이 있다고 가정하자.

로그인 시에 비밀번호를 확인하기 위해서 입력된 사용자ID에 대한 비밀번호를 검색했을 때, 단 하나의 결과를 얻어야만 올바른 비밀번호를 입력했는지 확인이 가능할 것이다. 만일 하나의 사용자ID에 대해서 두 개 이상의 비밀번호를 얻는다면 어떤 비밀번호가 맞는 것인지 알 수 없다.

 

HashMapEx1.java

더보기
package kr.co.dong.datastructure.hashMap_hashTable;

import java.util.*;

public class HashMapEx1 {
	public static void main(String[] args) {
		HashMap map = new HashMap();
		map.put("myId", "1234");
		map.put("asdf", "1111");
		map.put("asdf", "1234");
		
		Scanner scanner = new Scanner(System.in);	// 화면으로부터 라인단위로 입력받는다.
		
		while(true) {
			System.out.println("id와 password를 입력해주세요.");
			System.out.print("id : ");
			String id = scanner.nextLine().trim();
			
			System.out.print("password : ");
			String password = scanner.nextLine().trim();
			
			if(!map.containsKey(id)) {
				System.out.println("입력하신 id는 존재하지 않습니다."
						+ "다시 입력해주세요.");
				continue;
			}
			
			if(!map.get(id).equals(password)) {
				System.out.println("비밀번호가 일치하지 않습니다. 다시 입력해주세요.");
			} else {
				System.out.println("id와 비밀번호가 일치합니다.");
				break;
			}
		}// end of while
	}// end of main
}
/*
실행결과
id와 password를 입력해주세요.
id : asdf
password : 1111
비밀번호가 일치하지 않습니다. 다시 입력해주세요.
id와 password를 입력해주세요.
id : asdf
password : 1234
id와 비밀번호가 일치합니다.
*/

HashMap을 생성하고 사용자ID와 비밀번호를 키와 값의 쌍(pair)으로 저장한 다음, 입력된 사용자ID를 키로 HashMap에서 검색해서 얻은 값(비밀번호)을 입력된 비밀번호와 비교하는 예제이다.

HashMap을 생성하고 데이터를 저장

 

키(key) 값(value)
myId 1234
asdf 1234

3개의 데이터 쌍을 저장했지만 2개 밖에 저장되지 않은 이유는 중복된 키가 있기 때문이다. 세 번째로 저장한 데이터의 키인 "asdf"는 이미 존재하기 때문에 새로 추가되는 대신 기존의 값을 덮어썼다. 그래서 키 "asdf"에 연결된 값은 '1234'가 된다.

map은 값의 중복을 허용하지만 키는 중복을 허용하지 않기 때문에 저장하려는 두 데이터 중에서 어느 쪽을 키로 할 것인지를 잘 결정해야 한다.

|참고| Hashtable은 키(key)나 값(value)으로 null을 허용하지 않지만, HashMap은 허용한다. 그래서 'map.put(null,null);'이나 'map.get(null);'과 같이 할 수 있다.

HashMapEx2.java

더보기

 

package kr.co.dong.datastructure.hashMap_hashTable;

import java.util.*;

public class HashMapEx2 {
	public static void main(String[] args) {
		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();
		Iterator it = set.iterator();
		
		while(it.hasNext()) {
			Map.Entry e = (Map.Entry)it.next();
			System.out.println("이름 : "+ e.getKey()+", 점수 : "+ e.getValue());
		}
		
		set = map.keySet();
		System.out.println("참가자 명단 : " + set);
		
		Collection values = map.values();
		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());
		System.out.println("최고점수 : " + Collections.max(values));
		System.out.println("최저점수 : " + Collections.min(values));
		
	}
}
/*
실행결과
이름 : 안자바, 점수 : 90
이름 : 김자바, 점수 : 100
이름 : 강자바, 점수 : 80
이름 : 이자바, 점수 : 100
참가자 명단 : [안자바, 김자바, 강자바, 이자바]
총점 : 370
평균 : 92.5
최고점수 : 100
최저점수 : 80
 */
*/

HashMap의 기본적인 메서드를 이용해서 데이터를 저장하고 읽어오는 예제

entrySet()을 이용해서 키와 값을 함께 읽어 올 수도 있고 keySet()이나 values()를 이용해서 키와 값을 따로 읽어 올 수도 있다.

HashMapEx3.java

더보기
package kr.co.dong.datastructure.hashMap_hashTable;

import java.util.*;

public class HashMapEx3 {
	static HashMap phoneBook = new HashMap();
	public static void main(String[] args) {
		addPhoneNo("친구", "이자바", "010-1111-1111");
		addPhoneNo("친구", "김자바", "010-2222-2222");
		addPhoneNo("친구", "김자바", "010-3333-3333");
		addPhoneNo("회사", "김대리", "010-4444-4444");
		addPhoneNo("회사", "김대리", "010-5555-5555");
		addPhoneNo("회사", "박대리", "010-6666-6666");
		addPhoneNo("회사", "이과장", "010-7777-7777");
		addPhoneNo("세탁", "010-888-8888");
		
		printList();
	}
	static void addPhoneNo(String groupName, String name, String tel) {
		addGroup(groupName);
		HashMap group = (HashMap)phoneBook.get(groupName);
		group.put(tel, name);
	}
	// 그룹 추가 메서드
	static void addGroup(String groupName) {
		if(!phoneBook.containsKey(groupName)) {
			phoneBook.put(groupName, new HashMap());
		}
	}
	
	static void addPhoneNo(String name, String tel) {
		addPhoneNo("기타", name, tel);
	}
	
	// 전화번호부 전체를 출력하는 메서드
	static void printList() {
		Set set = phoneBook.entrySet();
		Iterator it = set.iterator();
		
		
		while(it.hasNext()) {
			Map.Entry e = (Map.Entry)it.next();
			
			Set subSet = ((HashMap)e.getValue()).entrySet();
			Iterator subIt = subSet.iterator();
			
			System.out.println(" * " +e.getKey()+"["+subSet.size()+"]");
			
			while(subIt.hasNext()) {
				Map.Entry subE = (Map.Entry)subIt.next();
				String telNo = (String)subE.getKey();
				String name = (String)subE.getValue();
				System.out.println(name + " " + telNo);
			}
			System.out.println();
		}
	}
}
/*
실행 결과
 * 기타[1]
세탁 010-888-8888

 * 친구[3]
이자바 010-1111-1111
김자바 010-3333-3333
김자바 010-2222-2222

 * 회사[4]
이과장 010-7777-7777
박대리 010-6666-6666
김대리 010-5555-5555
김대리 010-4444-4444
*/

 

HashMap은 데이터와 키와 값을 모두 Object타입으로 저장하기 때문에 HashMap의 값으로 다시 HashMap을 가질 수 있다. 이렇게 함으로써 하나의 키에 다시 복수의 데이터를 저장할 수 있다.

키 값을 전화번호로 한 이유는 복수의 이름이 존재 할 수 있기 때문

HashMapEx4.java

더보기
package kr.co.dong.datastructure.hashMap_hashTable;

import java.util.*;

public class HashMapEx4 {
	public static void main(String[] args) {
		String[] data = {"A","K","A","K","D","K","A","K","K","K","Z","D"};
		
		HashMap map = new HashMap();
		
		for(int idx = 0; idx < data.length; idx++) {
			if(map.containsKey(data[idx])) {
				Integer value = (Integer)map.get(data[idx]);
				map.put(data[idx], new Integer(value.intValue() + 1));
			}else {
				map.put(data[idx], new Integer(1));
			}
		}
		
		Iterator it = map.entrySet().iterator();
		
		while(it.hasNext()) {
			Map.Entry entry = (Map.Entry)it.next();
			int value = ((Integer)entry.getValue()).intValue();
			System.out.println(entry.getKey() + " : " + printBar('#', value) + " " + value);
		}
		
	}
	public static String printBar(char ch, int value) {
		char[] bar = new char[value];
		
		for(int idx = 0; idx < bar.length; idx++) {
			bar[idx] = ch;
		}
		return new String(bar);	// String(char[] chArr)
	}
}
/*
A : ### 3
D : ## 2
Z : # 1
K : ###### 6
*/

문자열을 하나씩 읽어서 HashMap에 키로 저장하고 값을 1로 저장

저장된 키와 대조하여 저장되어 있다면 값을 1씩 증가

증가시킨후 '#'문자를 이용하여 시각화

한정되지 않은 범위의 비순차적인 값들의 빈도수는 HashMap을 이용하여 구할 수 있다.

'Computer Science > DataStructure' 카테고리의 다른 글

TreeMap  (0) 2022.11.22
해싱과 해싱함수  (0) 2022.11.21
TreeSet  (0) 2022.11.18
HashSet  (0) 2022.11.17
Compareator와 Comparable  (0) 2022.11.15