본문 바로가기
멀티캠퍼스 풀스택 과정/Java의 정석

자바의 정석8-6 HashMap, Hashtable, Collections 요약

by 이쟝 2022. 1. 10.

HashMapHashtable – 순서 x, 중복(X, O)

 

 

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

- Hashtable(old)은 동기화 되어 있고, HashMap(new)은 동기화 되어 있지 않음(동기화의 유무)

- HashMap과 TreeMap 둘 중에 하나를 사용하면 됨!

 

HashMap

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

- 순서를 유지하려면, LinkedHashMap클래스를 사용하면 된다.

 

TreeMap

- 범위 검색과 정렬에 유리한 컬렉션 클래스(TreeSet과 같은 특성을 가짐)

- HashMap보다 데이터 추가, 삭제에 시간이 더 걸림(비교해가면서 저장하기 때문)

- Key Value값으로 저장한다는 것만 빼면 TreeSet과 동일함

 

HashMap의 키(key)와 값(value)

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

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

 

(key) 컬렉션 내의 키(key) 중에서 유일해야 한다.
(value) (key)와 달리 데이터의 중복을 허용한다.

 

HashMap map = new HashMap( );
map.put(“myId”, “1234”);
map.put(“asdf”, “1234”);
map.put(“asdf”, “1111”);

-> [ ‘myId’ : “1234” ] [ ‘asdf’ : “1111” ]

 

HashMap클래스의 구조

 

Entry[ ] table: Entry 배열에 keyvalue로 저장

 

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

 

-> 객체 배열로 바로 key와 벨류를 저장할 수 있지만, keyvalue를 하나로 묶어서 Entry 배열에 저장하는 게 객체지향적인 코드!

 


해싱(hashing)

- 해시함수(hash function)로 해시테이블(hash table)에 데이터를 저장하고 검색(읽기)

- 해시함수는 데이터가 저장되어 있는 곳을 알려 주기 때문에 다량의 데이터 중에서도 원하는 데이터를 빠르게 찾을 수 있다. 

- 해싱을 구현한 클래스: HashSet, HashMap, Hashtable(모두 hashCode 사용, Objects.hash( )로 오버라이딩)

- 해시테이블은 배열과 링크드리스트가 조합된 상태(배열의 장점인 접근성 + 링크드리스트의 장점인 변경 용이함)

 

2차원 배열로 생겨서 table이라고 함

-> 해시테이블: 해싱에서 사용하는 자료구조는 다음과 같이 배열 + 링크드리스트의 조합으로 되어있다. 

 


해시테이블에 저장된 데이터를 가져오는 과정

 

(1) 검색하고자 하는 값의 키로 해시함수를 호출한다.

(2) 해시코드(해시함수의 반환값)로 해당 값이 저장되어 있는 링크드 리스트를 찾는다.

(3) 링크드 리스트에서 검색한 키와 일치하는 데이터를 찾는다.

저장위치 -> 해시코드(hash code) -> 배열의 인덱스

 

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

서로 다른 키일지라도 같은 값의 해시코드를 반환할 수 있다. (75, 72 모두 data[7]에 속함)

 


HashMap의 주요 메서드

 

HashMap( ) HashMap 객체를 생성
HashMap(int initialCapacity) 지정된 값을 초기용량으로 하는 HashMap객체를 생성
HashMap(int initialCapacity, float loadFactor) 지정된 초기용량과 load factorHashMap객체를 생성
HashMap(Map m) 지정된 Map의 모든 요소를 포함하는 HashMap을 생성

 

Object put(Object key, Object, value) 지정된 키와 값을 HashMap에 저장
void putAll(Map m) Map에 저장된 요소를 HashMap에 저장
Object remove(Object key) HashMap에서 지정된 키로 저장된 값(객체)를 제거
Object replace(Object key, Object value) 지정된 키의 값을 지정된 객체(value)로 대체
boolean replace(Object key, Object oldValue, Object newValue) 지정된 키와 객체(oldValue)가 모두 일치하는 경우에만 새로운 객체(newValue)로 대체

 

Set entrySet( ) HashMap에 저장된 키와 값을 엔트리(키와 값의 결합)의 형태로 Set에 저장해서 반환
Set keySet( ) HashMap에 저장된 모든 키가 저장된 Set을 반환
Collection values( ) HashMap에 저장된 모든 값을 컬렉션의 형태로 반환

 

Object get(Object key) 지정된 키(key)(객체)을 반환. 못찾으면 null 반환
Object getOrDefault(Object key, Object defaultValue) 지정된 키(key)의 값(객체)을 반환한다. 키를 못찾으면, 기본 값(defaultValue)로 지정된 객체를 반환
boolean containsKey(Object key) HashMap에 지정된 키(key)가 포함 되어있는지 알려준다.(포함되어 있으면 true)
boolean containsValue(Object value) HashMap에 지정된 값(value)이 포함 되어있는지 알려준다.(포함되어 있으면 true)

 

int size( ) HashMap에 저장된 요소의 개수를 반환
boolean isEmpty( ) HashMap이 비어있는지 알려준다.
void clear( ) HashMap에 저장된 모든 객체를 제거
Object clone( ) 현재 HashMap을 복제해서 반환

 


예제1) map 안에 idpwd가 들어있는 HashTable을 작성하고 사용자가 idpwd를 올바르게 입력했는지 확인하는 프로그램 작성

 

import java.util.*;
public class Ex11_16 {
	public static void main(String[] args) {
		HashMap map = new HashMap();
		map.put("myId", "1234");
		map.put("asdf", "1111");
		System.out.println(map);  // {myId=1234, asdf=1111}
		map.put("asdf", "1234");
		System.out.println(map);  // {myId=1234, asdf=1234} 중복되면 마지막값으로 저장

		Scanner s = new Scanner(System.in); // 화면으로부터 라인단위로 입력받는다. 
		
		while(true) {
			System.out.println("id와 password를 입력해주세요.");
			System.out.println("id: ");
			String id = s.nextLine().trim();
			
			System.out.println("password: ");
			String pwd = s.nextLine().trim();
			System.out.println();
			
			if(!map.containsKey(id)) {
				System.out.println("입력하신 id는 존재하지 않습니다. 다시 입력해주세요.");
				continue;
			}
			
			if(!(map.get(id)).equals(pwd)) { // get()은 키(id)에 맞는 값(pwd)을 반환! 
				System.out.println("비밀번호가 일치하지 않습니다. 다시 입력해주세요.");
			} else {
				System.out.println("id와 비밀번호가 일치합니다.");
				break;
			}
		}// while
	}//main
}

 

예제2) 학생들의 성적을 입력 받아 참가자 명단, 총점, 평균, 최고점수와 최저점수 구하는 프로그램 작성

 

import java.util.*;
public class Ex11_17 {

	public static void main(String[] args) {
		HashMap map = new HashMap();
		map.put("김자바", new Integer(90)); 
		map.put("김자바", 100); 
		map.put(new String("이자바"), new Integer(100));
		map.put("강자바", 80);
		map.put(new String("안자바"), 90);
		
		// (1) entrySet()을 이용해서 map에 저장된 데이터를 읽어오는 방법
		Set set = map.entrySet();  
		Iterator it = set.iterator();  
		
		while(it.hasNext()) {
			Map.Entry en = (Map.Entry)it.next(); // Map 인터페이스, Entry 인터페이스(Map의 내부인터페이스)
			System.out.println("이름: " + en.getKey() + ", 점수: " + en.getValue());
		}
		
		// (2) keySet()을 이용해서 map에 저장된 key를 읽어오는 방법
		set = map.keySet(); // 이름만 가져오기
		System.out.println("참가자 명단: " + set);
		
		// (3) 총점을 구하기
		Collection values = map.values();  // HashMap에 저장된 모든 값을 컬렉션의 형태로 반환
		it = values.iterator();    // value를 iterator()를 사용해서 데이터 읽어오기
		
		int total = 0;  // 총점을 넣을 변수
		
		while(it.hasNext()) {
			int num = (int)it.next();  
			total += num;           
		}
		System.out.println("총점: " + total);   
		System.out.println("평균: " + (float)total/set.size());  // set = map.keySet( )
		System.out.println("최고점수: " + Collections.max(values)); // Collections values = map.values()
		System.out.println("최저점수: " + Collections.min(values));
	}
}

 

더보기

(1) entrySet(key + value) -> Set으로 변환

- Set으로 변환타입을 설정해줘야 Iterator를 사용할 수 있음

- set으로 iterator( )를 사용해서 데이터 읽어오기

- it.next( ) Map.Entry로 형변환을 해서 Map.Entry에 있는 key value값을 사용한다.

 

(2) keySet( ) -> set으로 변환

- 참가자 명단을 불러오기

 

(3) 총점을 구하기(value의 모든 값들)

- Collection values( )를 사용해 HashMap에 저장된 모든 값(values)를 컬렉션의 형태로 반환

- values으로 iterator( )를 사용해서 데이터 읽어 오기

- 숫자의 합을 구해야 하니까 it.next( ) int로 형변환해서 total 값에 넣어준다.

- 오토박싱, 언박싱을 사용해서 참조형, 기본형 둘 다 사용 가능하다. 하지만

 

Integer num = (Integer)it.next( ); 
total += num.intValue( );
int num = (int)it.next( );
total += num;
-> Int의 참조형 -> Int의 기본형

 

<출력값>

이름: 안자바, 점수: 90

이름: 김자바, 점수: 100

이름: 강자바, 점수: 80

이름: 이자바, 점수: 100

참가자 명단: [안자바, 김자바, 강자바, 이자바]

총점: 370

평균: 92.5

최고점수: 100

최저점수: 80


Collections – 컬렉션을 위한 메서드(static)를 제공

Objects, Arryas, Collections -> 유용한 메서드를 제공

 

1. 컬렉션 채우기, 복사, 정렬, 검색 – fill( ), copy( ), sort( ), binarySearch( )

 

2. 컬렉션의 동기화 – synchronizedXXX( )

 

 

 

-> ArrayListHashMap과 같은 컬렉션은 필요할 때만 java.util.Collections클래스의 동기화 메서드를 이용해서 동기화처리가 가능하도록 함.

-> 데이터의 일관성을 유지하기 이해서 공유되는 객체에 동기화(synchronization)가 필요

 

List syncList = Collections.synchronizedList(new ArrayList(…));
syncList -> 동기화된 Llist, new ArrayList -> 동기화 되지 않은 리스트

 

3. 변경불가(readOnly) 컬렉션 만들기 – unmodifiableXXX( )

 

 

- 컬렉션을 보호해야 할 때, 읽기전용으로 만든다.

 

4. 싱글톤 컬렉션 만들기 – singletonXXX( )

 

 

- 객체 1개만 저장하는 컬렉션으로 매개변수로 저장할 요소를 지정하면, 해당 요소를 저장하는 컬렉션을 반환.

 

5. 한 종류의 객체만 저장하는 컬렉션 만들기 – checkedXXX( )

 

 

List li = new ArrayList();
List checkedList = checkedList(li, String.class) // String만 사용가능
checkedList.add("abc");           // ok
checkedList.add(new Integer(3));  // 에러! ClassCastException생성
-> 한 가지 타입만 저장가능

예제) 컬렉션 메서드

 

import java.util.*;
import static java.util.Collections.*; /// Collections를 생략가능하게 해준다.
public class Ex11_19 {

	public static void main(String[] args) {
		List list = new ArrayList();
		System.out.println(list);  // []
		
		addAll(list, 1,2,3,4,5);   // 원래는 Collections.addALL인데 import collection을 해서 생략가능
		System.out.println(list);  // [1, 2, 3, 4, 5]
		
		rotate(list,1);            // 각 요소를 회전시킴(반 시계 방향으로 한번 회전)
		System.out.println(list);  // [5, 1, 2, 3, 4]
		
		swap(list, 0, 2);          // 첫 번째와 세 번째를 교환(swap)
		System.out.println(list);  // [2, 1, 5, 3, 4]

		shuffle(list);             // 저장된 요소의 위치를 임의로 변경(run할 때 마다 결과 바뀜)
		System.out.println(list);  // [1, 4, 5, 3, 2]
		
		sort(list, reverseOrder());  // 역순 정렬 reverse(list);와 동일
		System.out.println(list);    // [1, 2, 3, 4, 5]
		
		sort(list);                 // 정렬
		System.out.println(list);   // [1, 2, 3, 4, 5]
		
		int idx = binarySearch(list, 3); // 3이 저장된 위치(index)를 반환, binarySearch쓰기전에 정렬!
		System.out.println("index of 3 = " + idx);                 // index of 3 = 2
		
		System.out.println("max = " + max(list));                  // 최댓값 max = 5
		System.out.println("min = " + min(list));                  // 최솟값 min = 1
		System.out.println("min = " + max(list, reverseOrder()));  // 최솟값 min = 1
		
		fill(list, 7);               // list를 7로 채운다. 
		System.out.println(list);    // [7, 7, 7, 7, 7]
		
		// list와 같은 크기의 새로운 list를 생성하고 2로 채운다. 단 결과는 변경불가
		List newList = nCopies(list.size(), 2);
		System.out.println("newList=" + newList);     // newList=[2, 2, 2, 2, 2]
		
		System.out.println(disjoint(list, newList));  // 공통요소가 없으면 true, true
		
		copy(list, newList);  // newList에 있는 것을 list로 copy
		System.out.println("newList="+newList);    // newList=[2, 2, 2, 2, 2]
		System.out.println("list="+list);          // list=[2, 2, 2, 2, 2]
		
		replaceAll(list, 2, 1);                    // list에 있는 2의 값을 모두 1로 변경
		System.out.println("list=" + list);        // list=[1, 1, 1, 1, 1]
		
		Enumeration e = enumeration(list);  // iterator와 같음
		ArrayList list2 = list(e);
		
		System.out.println("list2=" + list2);      // list2=[1, 1, 1, 1, 1]

	}
}

 


Collection 클래스의 요약 및 정리

 

 

ArrayList 배열기반. 데이터의 추가와 삭제에 불리. 순차적인 추가삭제는 제일 빠름(연속적), 임의의 요소에 대한 접근성(accessibility)이 뛰어남
LinkedList 연결기반. 데이터의 추가와 삭제에 유리. 임의의 요소에 대한 접근성이 좋지 않음(불연속적)
HashMap 배열(ArrayList) + 연결(LinkedList)이 결합된 형태. 추가, 삭제, 검색, 접근성이 모두 뛰어남. 검색에는 최고성능을 보임
TreeMap 연결기반. 정렬과 범위검색(Binary Search)에 적합. 일반검색성능은 HashMap보다 떨어짐
Stack Vector를 상속받아 구현 객체 생성 가능 Stack s = new Stack( );
Queue LinkedListQueue인터페이스를 구현. Queue q = new LinkedList( );
Properties Hashtable을 상속받아 구현
HashSet HashMap을 이용해서 구현
TreeSet TreeMap을 이용해서 구현
LinkedHashMap
LinkedHashSet
HashMapHashSet에 저장순서유지기능을 추가