[Java] 해시맵(HashMap) 자료구조 정리

yujamint·2022년 7월 6일
2

Java

목록 보기
1/6

해시맵(HashMap) 이란?

해시맵은 이름 그대로 해싱(Hashing)된 맵(Map)이다. 해싱을 사용하기 때문에 많은 양의 데이터를 검색하는 데 있어서 뛰어난 성능을 보인다.

Map 인터페이스를 구현한 Map 컬렉션 중 하나이고, Map 인터페이스를 상속하고 있기 때문에 Map의 성질을 그대로 가지고 있다.

맵이란 키(Key)와 값(Value)으로 구성된 Entry 객체를 저장하는 구조를 가지고 있는 자료구조이다. 여기서 키와 값은 모두 객체이다. 키는 맵에 유일하게 있어야 되고, 중복을 허용하지 않지만 값은 중복된 값이어도 상관 없다. 같은 키의 값을 삽입하려고 하면 해당 키의 값이 변경된다.

해시맵원리
위 그림과 같이 HashMap은 내부에 키와 값을 저장하는 자료 구조를 가지고 있다. 해시 함수를 통해 키와 값이 저장되는 위치를 결정하므로, 그 위치를 알 수 없고 삽입되는 순서와 들어 있는 위치 또한 관계가 없다.(삽입한 순서에 따라 정렬되지 않는다.)


해시맵(HashMap) 사용법,예제

HashMap 선언

import java.util.HashMap;

HashMap<Integer,Integer> map1 = new HashMap<Integer,Integer>();
// Key - Integer / Value - Integer 타입의 Entry를 갖는 HashMap 선언

HashMap<Integer,Integer> map2 = new HashMap<>();
// New에서 타입 파라미터 생략 가능

HashMap<Integer,Integer> map3 = new HashMap<>(10);
// 초기 용량(capacity) 지정

HashMap<String,String> map4 = new HashMap<String,String>();
// Key - String / Value - String 타입의 Entry를 갖는 HashMap 선언

HashMap<String,String> map5 = new HashMap<String,String>(){{
    put("Key1", "Value1");
    put("Key2", "Value2");
}};
// 초기값 지정

HashMap을 생성하려면 위와 같이 키 타입과 값 타입을 파라미터로 주고 기본생성자를 호출한다. HashMap은 저장공간보다 값이 추가로 들어오면 List와 같이 저장공간을 추가로 늘리지만, List와 다르게 한 칸씩 늘리지 않고 약 두배로 늘리기 때문에 여기서 과부하가 많이 발생한다. 그렇기에 초기에 저장할 데이터 개수를 알고 있다면 Map의 초기 용량을 지정해주는 것이 좋다.

HashMap 값 추가

HashMap<Integer,String> hm = new HashMap<Integer,String>();
// Key - Integer / Value - Integer 타입의 Entry를 갖는 HashMap 선언

//값 추가
hm.put(1, "One");
hm.put(2, "Two");
hm.put(3, "Three");
hm.put(4, "Four");

System.out.println(hm);
//출력결과 : {1=One, 2=Two, 3=Three, 4=Four}

HashMap에 값을 추가하려면 위와 같이 Key와 Value를 파라미터로 주는 Put 메소드를 사용한다.
HashMap 선언 시에 설정한 타입과 같은 타입의 Key,Value를 넣어야 하며 입력하는 Key 값이 이미 내부에 존재한다면 기존의 Value는 새로 입력되는 Value로 변경된다.

HashMap 값 삭제

// HashMap 선언, 초기값 지정
HashMap<Integer,String> hm = new HashMap<Integer,String>(){{
    put(1, "One");
    put(2, "Two");
    put(3, "Three");
    put(4, "Four");
}};
hm.remove(1); // key값 1 제거
System.out.println(hm); // 출력결과 : {2=Two, 3=Three, 4=Four}
hm.clear(); // 모든 값 제거
System.out.println(hm); // 출력결과 : {}

HashMap에서 값을 제거하려면 key를 파라미터로 주는 remove(key) 메소드를 사용한다.
모든 값을 제거하려면 clear() 메소드를 사용하면 된다.

HashMap 값 출력

// HashMap 선언, 초기값 지정
HashMap<Integer,String> hm = new HashMap<Integer,String>(){{
    put(1, "One");
    put(2, "Two");
    put(3, "Three");
    put(4, "Four");
}};
System.out.println(hm); // 전체 출력 : {2=Two, 3=Three, 4=Four}
System.out.println(hm.get(3)); // Key 값 3의 Value 가져오기 : Three

//entrySet() 활용
for(Map.Entry<Integer,String> entry : hm.entrySet()) {
    System.out.println("Key :" + entry.getKey() + " Value :" + entry.getValue());
}//출력결과
//Key :1 Value :One
//Key :2 Value :Two
//Key :3 Value :Three
//Key :4 Value :Four

//keySet() 활용
for (int i : hm.keySet()) {
    System.out.println("Key :" + i + " Value :" + hm.get(i));
}//출력결과
//Key :1 Value :One
//Key :2 Value :Two
//Key :3 Value :Three
//Key :4 Value :Four

특정 Key의 Value를 가져오고 싶다면 get(key)를 사용하고, 전체를 출력하려면 entrySet() 또는 keySet() 메소드를 활용하여 Map의 객체를 반환받은 후 출력할 수 있다.
entrySet()은 Key와 Value로 구성된 Entry의 Set을 받기 때문에 Key와 Value가 모두 필요할 경우 사용한다.
keySet()은 Key의 Set을 반환받기 때문에 Key 값만 필요할 경우 사용하는데, 위의 코드와 같이 get(key) 메소드를 통해 Value까지 받아올 수도 있다.

  • keySet()을 활용하여 value까지 찾을 경우, key 값을 이용해서 value를 찾는 과정에서 시간이 많이 소모되기 때문에 많은 양의 데이터를 가져와야 한다면 entrySet()이 좋다.

HashMap의 put(key,value) 메소드와 get(key) 메소드를 이용해서 특정 key의 value를 순차적으로 증가시킬 수도 있다.

// HashMap 선언, 초기값 지정
HashMap<String, Integer> hm = new HashMap<String, Integer>() {{
    put("사과", 15);
    put("바나나", 12);
}};

위와 같이 key가 과일 이름이고 value가 과일의 개수인 HashMap이 있을 때, 과일 개수를 증가시키는 방법은 다음과 같다.

hm.put("사과", hm.get("사과")+1); // "사과" key의 value를 1 증가시킨다.
System.out.println(hm.get("사과")); // 사과 개수 : 16

원래 있던 key 값으로 put을 하면 value가 변경되는 것을 이용해서 (원래의 value값 + 1)을 value로 넣는다.
그렇다면, 원래 없던 key 값을 위의 코드로 value를 증가시켜보자.

hm.put("파인애플", hm.get("파인애플")+1);
System.out.println(hm.get("파인애플"));

위와 같이 원래 없던 key 값인 파인애플의 value를 1 증가시키는 코드를 입력하면 NullPointerException이 발생한다. hm에는 파인애플이라는 key가 존재하지 않고, get("파인애플") 메소드를 사용하면 Null이 발생하기 때문이다. 이를 방지하기 위해 우리는 getOrDefault(key, defaultValue) 메소드를 사용할 수 있다.

hm.put("파인애플", hm.getOrDefault("파인애플",0)+1);
System.out.println(hm.get("파인애플")); // 출력결과 : 1

getOrDefault는 key와 defaultValue를 파라미터로 받는 메소드로, 지정된 key에 매핑된 value가 없거나 null이면 defaultValue를 반환하는 메소드이다. 만약 위의 파인애플처럼 HashMap에 존재하지(매핑돼있지) 않는 key의 value를 가져오려고 시도하면 defaultValue를 기본값으로 HashMap에 key를 새로 만든다.

HashMap 크기 구하기

HashMap<Integer,String> hm = new HashMap<Integer,String>(){{
    put(1, "One");
    put(2, "Two");
    put(3, "Three");
    put(4, "Four");
}};
System.out.println(hm.size()); // HashMap 크기 출력 : 4

HashMap의 크기는 size() 메소드를 사용해서 구한다.

HashMap 특정 키 포함 여부

// HashMap 선언, 초기값 지정
HashMap<Integer,String> hm = new HashMap<Integer,String>(){{
    put(1, "One");
    put(2, "Two");
    put(3, "Three");
    put(4, "Four");
}};
System.out.println(hm.containsKey(4)); // Key 값 4 포함 여부 : true
System.out.println(hm.containsKey(5)); // Key 값 5 포함 여부 : false

특정 키가 존재하는지 존재하지 않는지 알고 싶을 때는 containsKey(key) 메소드를 사용한다.
존재한다면 true, 존재하지 않는다면 false를 리턴한다.
비슷한 메소드로, 특정 value가 존재하는지 확인할 수 있는 containsValue(value) 메소드가 있다.

HashMap.equals()

// HashMap 선언, 초기값 지정
HashMap<Integer,String> hm1 = new HashMap<Integer,String>(){{
    put(1, "One");
    put(2, "Two");
}};
HashMap<Integer,String> hm2 = new HashMap<Integer,String>(){{
    put(1, "One");
    put(2, "Two");
}};
HashMap<Integer,String> hm3 = new HashMap<Integer,String>(){{
    put(1, "One");
    put(3, "Three");
}};
System.out.println(hm1.equals(hm2)); // 출력결과 : true
System.out.println(hm1.equals(hm3)); // 출력결과 : false

두 Map이 같은지 비교하려면 두 Map의 매핑 상태가 같다면 true, 같지 않다면 false를 리턴하는 equals()메소드를 사용한다.


참고 자료(blog)
https://woodadada16.tistory.com/14
https://reakwon.tistory.com/151
https://coding-factory.tistory.com/556
https://crazykim2.tistory.com/587

profile
개발 기록

0개의 댓글