이 글은 유튜브 '자바의 정석 - 기초편'을 보고 정리한 글입니다.
📂content
1. HashMap과 Hashtable - 순서X, 중복(키X, 값O)
- Map 인터페이스를 구현. 데이터를 키와 값의 쌍으로 저장
- HashMap(동기화X)은 Hashtable(동기화O)의 신버전
TreeMap은 TreeSet처럼 이진탐색트리이다.
⚝ HashMap
- Map인터페이스를 구현한 대표적인 컬렉션 클래스
- 순서를 유지하려면, LinkedHashMap클래스를 사용하면 된다.
⚝ TreeMap
- 범위 검색과 정렬에 유리한 컬렉션 클래스
- HashMap보다 데이터 추가, 삭제에 시간이 더 걸림
2. HashMap의 키(key)와 값(value)
- 해싱(hashing)기법으로 데이터를 저장. 데이터가 많아도 검색이 빠르다.
- Map인터페이스를 구현. 데이터를 키와 값의 쌍으로 저장
키(key) 컬렉션 내의 키(key) 중에서 유일해야 한다.
값(value) 키(key)와 달리 데이터의 중복을 허용한다.
오른쪽과 같이 할 수도 있지만, 왼쪽 코드가 더 객체지향적인 코드이다. 근데 이건 예전 코드이다. 최신버전 jdk는 다르다. 그런데 이해하는데는 더 도움이 되기 때문에 안 바꿈.
3. 해싱(hashing)
간호사가 출생년도로 분류해서 환자정보를 관리한다고 하자.
만약, 주민번호를 알려주면서 환자정보를 달라고 하면, 간호사는 이를 통해 환자정보를 줄 수 있다.
어떤 키 값을 넣으면 배열의 index(저장위치)를 알려준다.
이것이 해시함수(hash function)이고 결과값이 hashcode이다.
그래서 같은 키를 넣으면 항상 같은 값이 나와야 한다.
- 해시함수(hash function)로 해시테이블(hash table)에 데이터를 저장, 검색
- 해시테이블은 배열과 링크드 리스트가 조합된 형태
이차원 배열처럼 생겨서 해시 테이블이라고 한다.
한 쪽이 링크드리스트인 이유는 변경하기 쉽게 하기 위해서이다.
배열은 index만 알면 찾아가기 쉽다는 장점이 있고, LinkedList는 변경에 유리하다는 장점이 있다.
그래서 이 둘을 합친 것이다.
⚝ 해시테이블에 저장된 데이터를 가져오는 과정
① 키로 해시함수를 호출해서 해시코드(배열의 인덱스/저장위치)를 얻는다.
② 해시코드(해시함수의 반환값)에 대응하는 링크드리스트를 배열에서 찾는다.
③ 링크드리스트에서 키와 일치하는 데이터를 찾는다.
※ 해시함수는 같은 키에 대해 항상 같은 해시코드를 반환해야 한다. (저장할 때와 읽어올 때와 일치해야하기 때문)
서로 다른 키일지라도 같은 값의 해시코드를 반환할 수도 있다.
4. HashMap - 주요 메서드
⚝ 생성자
HashMap()
HashMap(int initialCapacity)
HashMap(int initialCapacity, float loadFactor)
HashMap(Map m)
해시맵은 해시테이블의 데이터를 저장한다.
해시테이블 = 배열 + 링크드리스트
배열과 관련된 것은 initialCapacity와 loadFactor가 있다.
⚝ 그 외
Object put(Object key, Object value)
void putAll(Map m)
Object remove(Object key)
Object replace(Object key, Object value)
boolean replace(Object key, Object oldValue, Object newValue)
Set entrySet()
Set keySet()
Collection values()
Object get(Object key)
Obejct getOrDefault(Object key, Object defaultValue)
boolean containsKey(Object key)
boolean containsValue(Object value)
int size()
boolean isEmpty()
void clear()
Object clone() : 복제
⍟실습
EX11_16
package etc;
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");
//key값을 중복해서 사용한다고 해서 에러가 발생하지 않고, 마지막 값으로 변경된다.
map.put("asdf", "1234");
Scanner s = new Scanner(System.in); // 화면으로부터 라인단위로 입력받는다.
while(true) {
System.out.println("id와 password를 입력해주세요.");
System.out.print("id :");
//trim() : 앞 뒤 공백 삭제
String id = s.nextLine().trim(); //"1234 " -> "1234"
System.out.print("password :");
String password = s.nextLine().trim();
System.out.println();
if(!map.containsKey(id)) {
System.out.println("입력하신 id는 존재하지 않습니다. 다시 입력해주세요.");
continue;
}
if(!(map.get(id)).equals(password)) {
System.out.println("비밀번호가 일치하지 않습니다. 다시 입력해주세요.");
} else {
System.out.println("id와 비밀번호가 일치합니다.");
break;
}
} // while
} // main의 끝
}
EX11_17
package etc;
import java.util.*;
public class Ex11_17 {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("김자바", 90);
map.put("김자바", 100);
map.put("이자바", 100);
map.put("강자바", 80);
map.put("안자바", 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()) {
int i = (int)it.next();
total += i;
}
System.out.println("총점 : " + total); //총점 : 370
System.out.println("평균 : " + (float)total/set.size()); //평균 : 92.5
System.out.println("최고점수 : " + Collections.max(values)); //최고점수 : 100
System.out.println("최저점수 : " + Collections.min(values)); //최저점수 : 80
}
}
- Entry는 Map의 내부 인터페이스이다.
Entry 코드를 살펴보면 `interface Entry <K, V>` 로 되어있다.
앞에 static이 붙어있지 않지만 이는 생략된 것이다.
인터페이스 안에 선언된 내부 클래스나 인터페이스는 모두 static이 붙는다.
그래서 Map.Entry로 표현하는 것
EX11_18
package etc;
import java.util.*;
public class Ex11_18 {
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 i=0; i < data.length; i++) {
if(map.containsKey(data[i])) {
int value = (int)map.get(data[i]);
map.put(data[i], value+1); // 기존에 존재하는 키면 기존 값을 1증가
} else {
map.put(data[i], 1); // 긱존에 존재하지 않는 키는 값을 1로 저장
}
}
Iterator it = map.entrySet().iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
int value = (int)entry.getValue();
System.out.println(entry.getKey() + " : " + printBar('#', value) + " " + value );
/**
* A : ### 3
* D : ## 2
* Z : # 1
* K : ###### 6
*/
}
} // main
public static String printBar(char ch, int value) {
char[] bar = new char[value];
for(int i=0; i < bar.length; i++)
bar[i] = ch;
return new String(bar); // String(char[] chArr)
}
}
출처
'🎥Back > 자바의 정석' 카테고리의 다른 글
[JAVA의 정석]Generics, Generics 클래스 (0) | 2024.01.18 |
---|---|
[JAVA의 정석]Collections 클래스, 컬렉션 클래스 요약 (2) | 2024.01.17 |
[JAVA의 정석]TreeSet (0) | 2024.01.17 |
[JAVA의 정석]HashSet (0) | 2024.01.17 |
[JAVA의 정석]Comparator와 Comparable (0) | 2024.01.17 |