Map 컬렉션은 Key와 Value 쌍으로 구성된 Entry객체를 저장한다. 이때, 키와 값은 모두 객체이며 Key는 중복 저장할 수 없다. 만약 기존에 저장되어 있는 Key값으로 새로운 값을 저장하면 기존 값은 없어지고 새로운 값으로 저장된다.
Map Collection의 구현 클래스 : HashMap, Hashtable, LinkedHashMap, Properties, TreeMap, ..
K, V : 타입 파라미터로 Map 인터페이스가 제네릭 타입이기 때문에 구체적 타입이 구현 객체 생성시 결정됨
1) 객체 추가
2) 객체 검색
3)객체 삭제
1) keySet() 메소드 사용
Set<String> keySet = hashMap.keySet();
Iterator<String> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
String key = keyIterator.next();
Integer value = hashMap.get(key);
}
2) entrySet() 메소드 사용
Map.Entry를 Set 컬렉션으로 가져와, 반복자를 사용해서 Map.Entry를 하나씩 얻고 키와 값을 얻는다.
Set<Map.Entry<String, Integer>> entrySet = hashMap.entrySet();
Iterator<Map.Entry<String, Integer>> entryIterator = entrySet.iterator();
while(entryIterator.hashNext()) {
Map.Entry<String, Integer>> entry = entryIterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
}
HashMap에서 동일한 키가 되는 조건은 hashCode()의 리턴값이 동일하고, equals() 메소드가 true이어야 한다.
키 타입으로 String을 사용할 때, 문자열 내용이 같을 경우 동등 객체가 될 수 있도록 hashCode()와 equals() 메소드가 재정의 되어있다.
만일 HashMap의 키로 사용자 객체를 사용할 경우, hashCode()와 equals() 메소드를 재정의해 동등 객체가 될 조건을 정해야 한다.
//K : 키 타입, V : 값 타입
HashMap<K, V> hashMap = new HashMap<K, V>();
이때, K와 V는 primitive type(byte, short, int, bolean, char, ..)을 사용할 수 없으며 클래스 및 인터페이스 타입만 가능하다.
String[] participant 배열에 이름이 String 타입으로저장되어 있다고 할 경우, 이름 당 사람 수를 구하는 코드.
ex) participant = {"Song", "Kim", "Jun", "Kim"};
일 경우 다음과 같이 hashMap을 저장해야 한다.
Key | Value |
---|---|
Song | 1 |
Kim | 2 |
Jun | 1 |
HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
int peopleCnt = 0;
int i;
//사람들을 이름당 몇명인지로 저장
for(i=0; i<participant.length; i++) {
if(hashMap.containsKey(participant[i])) { //동일한 이름이 있으면
peopleCnt = hashMap.get(participant[i]); //기존 몇명인지 알아내고
//새로운 값으로 저장
hashMap.put(participant[i], new Integer(peopleCnt+1));
} else { //동일한 이름이 아직 없으면
hashMap.put(participant[i], new Integer(1));
}
}
//이름 당 사람의 수를 출력
Set<String> keySet = hashMap.keySet();
Iterator<String> keyIterator = keySet.iterator();
while(keyIterator.hasNext()) {
String key = keyIterator.next();
Integer value = hashMap.get(key);
System.out.println("Key / Value : " + key + " / " + value);
}
이름과 나이를 저장하는 Person 객체를 키로하고, 사람 번호를 value로 저장한다.
import java.util.HashMap;
public class HashMapEx {
public static class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//이름과 나이가 같으면 true를 리턴하도록 재정의
@Override
public boolean equals(Object obj) {
if(obj instanceof Person) {
Person person = (Person) obj;
return (this.age == person.age) && (this.name.equals(person.name));
} else {
return false;
}
}
//이름과 나이가 같으면 동일한 값을 리턴하도록 재정의
@Override
public int hashCode() {
return name.hashCode() + age;
}
}
public static void main(String[] args) {
HashMap<Person, Integer> hashMap = new HashMap<Person, Integer>();
hashMap.put(new Person("김김김", 24), 1);
hashMap.put(new Person("김김김", 24), 1);
System.out.println("Entry 수 : " + hashMap.size());
}
}
출력 = Entry 수 : 1
HashMap과 동일한 내부 구조를 가지고 있으므로 키로 사용할 객체는 hashCode()와 equals() 메소드를 재정의해 동등 객체가 될 조건을 정해야 한다.
Hashtable<K, V> hashtable = new HashTable<K, V>();
키보드로 아이디와 비밀번호를 입력받아, Hashtable에 저장되어 있는 키(아이디)와 값(비밀번호)를 비교한 후 로그인 여부를 출력
import java.util.Hashtable;
import java.util.Scanner;
public class HashtableEx {
public static void main(String[] args) {
Hashtable<String, String> hashtable = new Hashtable<String, String>();
hashtable.put("apple", "123");
hashtable.put("grape", "1234");
hashtable.put("strawberry", "12345");
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("아이디와 비밀번호 입력하세요");
System.out.print("아이디 : ");
String id = scanner.nextLine();
System.out.print("비밀번호 : ");
String password = scanner.nextLine();
System.out.println();
if (hashtable.containsKey(id)) {
if (hashtable.get(id).equals(password)) {
System.out.println("로그인 되었습니다.");
break;
} else {
System.out.println("비밀번호가 일치하지 않습니다.");
}
} else {
System.out.println("아이디가 존재하지 않습니다.");
}
}
}
}
실행결과
아이디와 비밀번호 입력하세요
아이디 : apple
비밀번호 : 123로그인 되었습니다.
Properties는 Hashtable의 하위 클래스로 Hashtable의 모든 특징을 가진다. 반면 Properties는 키와 값을 String 타입으로 제한한 컬렉션이며 애플리케이션의 옵션 정보, 데이터베이스의 연결 정보, 국제화(다국어) 정보가 저장된 (~.properties) 파일을 읽을 때 주로 사용한다.
프로퍼티 파일은 키와 값이 = 기호로 연결되어 있는 텍스트 파일로 ISO 8859-1 문자셋으로 저장되며, 이 문자셋으로 직접 표현할 수 없는 한글은 유니코드로 변환돼 저장된다.
프로퍼티 파일 읽는 방법
Properties 객체를 생성하고, load() 메소드를 호출한다.
load() 메소드는 프로퍼티 파일로부터 데이터를 읽기 위해 FileReader 객체를 파라미터로 받는다.
Properties properties = new Properties();
properties.load(new FileReader("C:/~/database.properties"));
Properties 객체에서 키의 값을 읽는 방법
Properties 객체에서 해당 키의 값을 읽으려면 getProperty() 메소드를 사용한다.
String value = properties.getProperty("key");
TreeMap은 이진 트리를 기반으로 한 Map 컬렉션이다.
: TreeMap은 키와 값이 저장된 Map.Entry를 저장한다. 저장 시, 키 값을 기준으로 자동 정렬해 저장하는데 부모 키값을 기준으로 키 값이 낮은(작은) 것은 왼쪽 자식에, 키 값이 높은(큰) 것은 오른쪽 자식 노드에 저장한다.
따라서 특정 객체를 찾거나 범위 검색 시 매우 유용하다.
Return type | Method | 설명 |
---|---|---|
Map.Entry<K, V> | firstEntry() | 제일 낮은(작은) Map.Entry 리턴 |
Map.Entry<K, V> | lastEntry() | 제일 높은(큰) Map.Entry 리턴 |
Map.Entry<K, V> | lowerEntry(K key) | 주어진 키보다 바로 아래의 Map.Entry 리턴 |
Map.Entry<K, V> | higherEntry(K key) | 주어진 키보다 바로 위 Map.Entry 리턴 |
Map.Entry<K, V> | floorEntry(K key) | 주어진 키와 같은 키가 있으면 해당 Map.Entry를 리턴하고, 없으면 주어진 키 바로 아래의 Map.Entry를 리턴 |
Map.Entry<K, V> | ceilingEntry(K key) | 주어진 키와 같은 키가 있으면 해당 Map.Entry를 리턴하고, 없으면 주어진 키 바로 위의 Map.Entry를 리턴 |
Map.Entry<K, V> | pollFirstEntry() | 제일 낮은 Map.Entry를 꺼내고 컬렉션에서 제거 |
Map.Entry<K, V> | pollLastEntry() | 제일 높은 Map.Entry를 꺼내고 컬렉션에서 제거 |
Return type | Method | 설명 |
---|---|---|
NavigableSet< K> | descendingKeySet() | 내림차순으로 정렬된 키의 NavigableSet 리턴 |
NavigableMap<K, V> | descendingMap() | 내림차순으로 정렬된 Map.Entry의 NavigableMap을 리턴 |
만일 오름차순으로 정렬하고 싶을 경우 descendingMap() 메소드를 두 번 호출해서 사용할 수 있다.
NavigableMap<K, V> descendingMap = treeMap.descendingMap();
NavigableMap<K, V> ascendingMap = descendingMap.descendingMap();
NavigableMap<K, V> headMap(K toKey, boolean inclusive)
inclusive가 true일 경우 : 찾는 Map.Entry <= 끝 Map.Entry
inclusive가 flase일 경우 : 찾는 Map.Entry < 끝 Map.Entry
NavigableMap<K, V> tailMap(K fromKey, boolean inclusive)
inclusive가 true일 경우 : 시작 Map.Entry <= 찾는 Map.Entry
inclusive가 flase일 경우 : 시작 Map.Entry < 찾는 Map.Entry
NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
fromInclusive : true일 경우 시작 Map.Entry 포함
toInclusive : true일 경우 끝 Map.Entry 포함