728x90
반응형
HashMap
- HashMap 이 어떻게 동작하고 저장하는지 알아본다.
- HashMap 에서 어떻게 비교연산이 이루어 지는지 알아본다.
- equals 와 hashCode 함수에 대해 알아본다
HashMap?
- HashMap 이란 키와 값을 묶어서 하나의 데이터로 저장한다.
- 그리고 hashing 을 사용하기 때문에 데이터를 검색하는데 성능이 뛰어나다.
- hashing 이란 뭘까?
Hashing
해싱이란 해시함수를 이용해서 데이터를 해시 테이블에 저장하고
검색하는 기법이다.
그림
- 1. 키값을 해시함수에 넣는다.
- 2. 해시함수 결과 값(해시코드)에 해당하는 링크드리스트를 찾는다.
- 3. 링크드 리스트에 검색한 키와 일치하는 데이터를 저장하거나 또는 데이터를 찾는다.
hashCode?
- Map 을 사용할때 때때로 hashCode를 사용하면 직접 해쉬 값을 만들수 있다.
- 왜 직접 해쉬 코드를 만들어야 하는 경우가 생길까?
- 예제를 보자
예제
- 억지스럽지만 멤버가 있고
- 멤버가 어느 나라 사람인지 국가 코드를 이용한다.
- 멤버를 키로 국가코드를 값으로 Map 에 저장한다.
코드
public class Member {
private String name;
private int age;
private String nickName;
public Member(String name, int age, String nickName) {
this.name = name;
this.age = age;
this.nickName = nickName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Member member1 = new Member("skahn",150, "Algorithm");
Member member2 = new Member("skahn",150, "Algorithm");
Map<Member, String> members = new HashMap<>();
members.put(member1, "KR");
members.put(member2, "KR");
System.out.println("members map size is "+members.size());
}
}
결과
Member1 hashcode: 1101288798
Member2 hashcode: 942731712
members map size is 2
문제점
- 맵안에 데이터가 2개 저장되었다.
- 코드를 보면 멤버에 대한 내용은 똑같다 즉 사용자의 실수로 중복저장이 되었다.
- HashCode 값이 다르기 때문이다.
hashCode 오버라이딩
hashCode 를 오버라이딩한다면 직접 해쉬코드를 생성할수 있다.
코드
public class Member {
private String name;
private int age;
private String nickName;
public Member(String name, int age, String nickName) {
this.name = name;
this.age = age;
this.nickName = nickName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int number = 31;
int hashCode = 1;
hashCode = number * hashCode + ((name == null) ? 0 : name.hashCode());
hashCode = number * hashCode + ((nickName == null) ? 0 : nickName.hashCode());
hashCode = number * hashCode + age;
return hashCode;
}
}
public class Main {
public static void main(String[] args) {
Member member1 = new Member("skahn",150, "Algorithm");
Member member2 = new Member("skahn",150, "Algorithm");
Map<Member, String> members = new HashMap<>();
members.put(member1, "KR");
members.put(member2, "KR");
System.out.println("members map size is "+members.size());
}
}
결과
Member1 hashcode: -1761403243
Member2 hashcode: -1761403243
members map size is 2
문제점
- 동일한 내용에 대해서 hash 값은 같아졌다.
- 하지만 똑같이 맵안에 데이터가 2개 저장되었다.
- 뭐가 문제일까?
- equasl 도 역시 오버라이딩을 해줘야 한다.
반응형
HashMap의 equals 함수?
- 일단 map 의 equals 함수를 열어보면
- 객체로 비교한다는 것을 알수 있다.
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
- 우리는 member1 member2 를 각각 생성했다.
- HashMap에 저장될때 비교를 수행하게 된다.
- hashcode 가 같아도 객체를 비교했을때 다르다고 나온다.
- 그렇기 때문에 두번 저장됐던 것이다.
equals 오버라이딩
자그럼 equals 함수를 오버라이딩 해서
중복체크를 다시 해보자.
코드
public class Member {
private String name;
private int age;
private String nickName;
public Member(String name, int age, String nickName) {
this.name = name;
this.age = age;
this.nickName = nickName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int number = 31;
int hashCode = 1;
hashCode = number * hashCode + ((name == null) ? 0 : name.hashCode());
hashCode = number * hashCode + ((nickName == null) ? 0 : nickName.hashCode());
hashCode = number * hashCode + age;
return hashCode;
}
@Override
public boolean equals(Object o) {
// 객체가 동일하다면 true 중복저장 안됨
if (this == o) return true;
// 객체가 널이거나 클래스 값이 다르면 다르기 때문에 false
// member인 경우 클래스가 둘다 동일하기 때문에 그냥 넘어간다.
if (o == null || getClass() != o.getClass()) return false;
// 멤버 값 비교
Member member = (Member) o;
// 나이, 이름, 닉네임을 비교해서 다른지 체크한다.
return age == member.age && Objects.equals(name, member.name) && Objects.equals(nickName, member.nickName);
}
}
public class Main {
public static void main(String[] args) {
Member member1 = new Member("skahn",150, "Algorithm");
Member member2 = new Member("skahn",150, "Algorithm");
Map<Member, String> members = new HashMap<>();
members.put(member1, "KR");
members.put(member2, "KR");
System.out.println("members map size is "+members.size());
}
}
결과
- 우리가 원하는 대로 동일한 내용에 대한 멤버는 한번만 저장되었다.
Member1 hashcode: -1761403243
Member2 hashcode: -1761403243
members map size is 1
결론
- hashCode 를 직접 만들수 있다.
- equals 를 오버라이딩 할수 있다.
- hashCode 나 equals 를 오버라이딩을 하면
- 둘다 오버라이딩을 해줘야한다.
- 다음과 같은 순으로 HashMap에 저장이 이루어진다.
- HashMap에 값이 없을 경우 HashCode 를 생성후 저장
- HashMap에 값이 있을 경우 HashCode 를 생성후 비교연산을 거친다
728x90
반응형
'Language > Java' 카테고리의 다른 글
[Java] BigDecimal (2) | 2021.12.11 |
---|---|
[Java] Generic Type Erasure (0) | 2021.10.29 |
[Java] getter 와 setter (0) | 2021.09.05 |
[Java] Null 안전하게 다루기 (0) | 2021.08.24 |
[Java] 직렬화, 역직렬화 (0) | 2021.08.23 |
댓글