자료를 쌍으로 관리하는데 필요한 메서드들이 Map 인터페이스에 정의되어 있다. key-value 쌍으로 이루어진 객체의 key 값은 유일하며 value 값은 중복될 수 있다.
HashMap
은 Map 인터페이스를 구현한 클래스 중 가장 많이 사용한다. HashMap
에서 자료를 관리하는 방식은 해시 방식이다.
해시 : 정보를 저장하거나 검색할 때 사용하는 자료 구조.
정보를 어디에 저장할 것인지, 어디서 가져올 것인지 해시 함수를 이용해 구현
-> 해시 함수 : 객체 특정 정보를 매개변수 값으로 넣으면 그 객체가 저장되어야 할 위치나 해시 테이블 주소 반환
hashcode = hash(key); //객체의 해시 코드 값(메모리 위치 값) 반환됨
해시 방식 자료를 저장하는 공간을 해시 테이블이라고 한다. key
값이 정해지면 그에 대응하는 해시 테이블의 저장 위치가 정해지는데 이런 위치를 계산하는 함수를 해시 함수라고 한다.
새로운 key-value 자료가 입력되거나, key를 알고 있는 상태에서 value를 검색하는 데 걸리는 시간은 산술적으로 계산할 수 있다. 그러므로 자료 추가 속도나 검색 속도가 상당히 빠르다는 장점이 있다. 하지만 서로 다른 key값에 같은 index가 반환되는 충돌이 발생하는 경우도 있다. 따라서 해시 테이블에 데이터를 꽉 채우지 않고 적정 수준이 되면 테이블을 확장해 충돌 발생 확률을 낮춰준다.
Map 인터페이스에서 사용하는 key 값은 중복될 수 없기에 equals()
, hashcode()
메서드를 재정의해 사용하는 것이 좋다. equals()
메서드를 재정의했다면 hashcode()
메서드도 재정의 해야한다.
class Fruit{
String name;
public Fruit(String name){
this.name = name;
}
@Override
public boolean equals(Object o){
if(o instanceof Fruit){
return ((Fruit)o).name.equals(name);
return false;
}
@Override
public int hashCode(){
return name != null ? name.hashCode() : 0;
}
@Override
public String toString(){
return String.format("Fruit(%s)", name);
}
}
import java.util.*;
public class HashMapDemo{
public static void main(String[] args){
Map<Fruit, Integer> map = new HashMap<>();
map.put(new Fruit("사과"), 5);
map.put(new Fruit("사과"), 2);
map.put(null, 3);
System.out.println(map.size());
System.out.println(map);
}
}
equals()
, hashCode()
오버라이딩하지 않은 경우 :equals()
, hashCode()
오버라이딩할 경우 : HashMap
과 Hashtable
클래스는 모두 쌍으로 이루어진 자료를 관리하는데 사용된다.
map.put(key, value)
map.clear()
, map.remove(key)
변수명
get(key)
entrySet()
map.containsKey(key)
putAll()
HashMap<Integer,String> map = new HashMap<Integer,String>(){{
put(1,"사자");
put(2,"치타");
}};
System.out.println(map); // {1=사자, 2=치타}
System.out.println(map.get(1));//key값 1의 value - 사자
for (Entry<Integer, String> entry : map.entrySet()) {
System.out.println("[Key]:" + entry.getKey() + " [Value]:" + entry.getValue());
}
//[Key]:1 [Value]:사자
//[Key]:2 [Value]:치타
for(Integer i : map.keySet()){ //저장된 key값 확인
System.out.println("[Key]:" + i + " [Value]:" + map.get(i));
}
//[Key]:1 [Value]:사자
//[Key]:2 [Value]:치타
if(!map.containsKey(2){ //해당 키가 없으면 덮어씀.
map.put(2, "호랑이"); //이 경우 해당 키가 존재하기에 값 변화X
}
HashMap<Integer,String> map2 = new HashMap<Integer,String>(){{
put(1,"코끼리");
put(2,"뱀");
}};
map2.putAll(map); //map2에 map을 합침.
Map 인터페이스를 구현한 클래스 중 key 값으로 자료를 정렬하려면 TreeMap
을 사용할 수 있다. TreeMap
은 TreeSet
과 마찬가지로 이진 검색 트리로 구현되었다. key 값으로 정렬하므로 key 값에 해당하는 클래스에 Comparable
이나 Comparator
인터페이스를 구현해야 한다.
HashMap
과 Hashtable
은 내부 구조가 거의 같지만, 다른 점이 있다. Hashtable
은 HashMap
과 달리 동기화된 메서드로 구현되어 스레드에 안전하며, HashMap
에서는 키와 값으로 null
을 사용할 수 있지만 Hashtable
에서는 사용할 수 없다. 일반적으로 동기화가 필요없다면 굳이 Hashtable
을 사용하지 않아도 된다.