๐น equals()์ hashCode()๋ ?
Java์์ ๋ชจ๋ ํด๋์ค๋ Object ํด๋์ค๋ฅผ ์์๋ฐ์ผ๋ฉฐ, equals()์ hashCode() ๋ฉ์๋๋ ์ด Object ํด๋์ค์ ์ ์๋์ด ์๋ค.
์ฆ, Java์ ๋ชจ๋ ๊ฐ์ฒด๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, ํ์์ ๋ฐ๋ผ ์ค๋ฒ๋ผ์ด๋ฉํด์ ์ฌ์ฉํ ์ ์๋ค.
๐น equals()
public boolean equals(Object obj)
- equals()๋ ๋ ๊ฐ์ฒด๊ฐ ๋ ผ๋ฆฌ์ ์ผ๋ก ๊ฐ์์ง(๋๋ฑ์ฑ) ๋น๊ตํ ๋ ์ฌ์ฉ๋๋ค.
- ํ์ง๋ง ๊ธฐ๋ณธ ๊ตฌํ์์๋ ๋จ์ํ ๋์ผ์ฑ(Identity)์ ๋น๊ตํ๋ ๋ฐฉ์์ผ๋ก ๋์ด์๋ค.
- ์ฆ, this == obj์ ๋์ผํ๋ค.
public boolean equals(Object obj) {
return (this == obj);
}
๐ธ ๋์ผ์ฑ(Identity) vs ๋๋ฑ์ฑ(Equality)
- ๋์ผ์ฑ(Identity) : == ์ฐ์ฐ์์ฒ๋ผ ๋ ๊ฐ์ฒด๊ฐ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ์ฐธ์กฐํ๋์ง ๋น๊ต
- ๋๋ฑ์ฑ(Equality) : ๊ฐ์ฒด๊ฐ ๊ฐ์ง ๊ฐ์ด ๊ฐ์์ง ๋น๊ต
→ ์ผ๋ฐ์ ์ผ๋ก ๊ฐ์ด ๊ฐ๋ค๊ณ ํ๋จ๋๋ ๊ฐ์ฒดํ๋ฉด, equals()๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์ ๋๋ฑ์ฑ์ ๊ธฐ์ค์ผ๋ก ๋น๊ตํด์ผ ํ๋ค.
โ String ํด๋์ค์ equals() ์ค๋ฒ๋ผ์ด๋ฉ ์์
String s1 = new String("Test");
String s2 = new String("Test");
System.out.println(s1 == s2); // false (๋ค๋ฅธ ๊ฐ์ฒด)
System.out.println(s1.equals(s2)); // true (๊ฐ์ด ๊ฐ์)
String ํด๋์ค๋ equals() ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์, ๋ด๋ถ ๋ฌธ์ ๋ฐฐ์ด์ ๊ฐ์ด ๊ฐ์์ง๋ฅผ ๋น๊ตํ๋๋ก ๊ตฌํ๋์ด ์๋ค.
public boolean equals(Object anObject) {
if (this == anObject) return true;
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char[] v1 = value;
char[] v2 = anotherString.value;
for (int i = 0; i < n; i++) {
if (v1[i] != v2[i]) return false;
}
return true;
}
}
return false;
}
๐น hashCode()
hashCode() ๋ฉ์๋๋ ๊ฐ์ฒด์ ํด์ ๊ฐ์ ๋ํ๋ด๋ ์ ์(int)๋ฅผ ๋ฐํํ๋ค.
์ด ๊ฐ์ ํด์ ๊ธฐ๋ฐ ์ปฌ๋ ์ (HashMap, HashSet ๋ฑ)์์ ๊ฐ์ฒด์ ์ ์ฅ ์์น๋ฅผ ๊ฒฐ์ ํ ๋ ์ฌ์ฉํ๋ค.
โ ๊ธฐ๋ณธ ๊ตฌํ
Object ํด๋์ค์ hashCode()๋ ๊ฐ์ฒด์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํด์๊ฐ์ ์์ฑํ๋ค.
๋ค๋ง, ์ด ๋ฐฉ์์ JVM ๊ตฌํ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์๋ค.
public native int hashCode();
์ฌ๊ธฐ์ native ํค์๋๋ ์ด ๋ฉ์๋๊ฐ Java๊ฐ ์๋ C/C++ ๊ฐ์ ์ธ์ด๋ก ์์ฑ๋์๊ณ ,
JNI(Java Native Interface)๋ฅผ ํตํด ํธ์ถ๋๋ค๋ ์๋ฏธ์ด๋ค.
→ ์ผ๋ฐ ๊ฐ๋ฐ์๊ฐ ์ด ๋ฉ์๋์ ๋ด๋ถ๋ฅผ ์์ ํ๊ฑฐ๋ ์ง์ ๊ตฌํํ ์๋ ์๋ค.
๐ธ hashCode() ์ญํ
- HashMap, HashSet, Hashtable ๋ฑ ํด์ ๊ธฐ๋ฐ ์๋ฃ๊ตฌ์กฐ์์ ์ฌ์ฉ๋จ
- ๊ฐ์ฒด์ ์ ์ฅ ์์น๋ฅผ ๊ณ์ฐํ ๋ ํ์ฉ๋จ
- ๋น ๋ฅธ ๊ฒ์, ์ฝ์ , ์ญ์ ๋ฅผ ์ํ ํ์์ ์ธ ๋ฉ์๋
๐ธ equals()์ hashCode()์ ๊ด๊ณ
equals()๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ ๊ฒฝ์ฐ, ๋ฐ๋์ hashCode()๋ ํจ๊ป ์ค๋ฒ๋ผ์ด๋ํด์ผ ํ๋ค.
๊ทธ๋ ์ง ์์ผ๋ฉด ๋ ผ๋ฆฌ์ ์ผ๋ก ๊ฐ์ ๊ฐ์ฒด๋ผ๋ ํด์ ๊ธฐ๋จ ์ปฌ๋ ์ ์์๋ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๋ก ์ธ์๋ ์ ์๋ค.
๐ธ ๋ ๋ฉ์๋์ ํ์ ๊ท์น
- ๊ฐ์ ๊ฐ์ฒด๋ ํญ์ ๊ฐ์ hashCode๋ฅผ ๊ฐ์ ธ์ผ ํ๋ค.
- obj1.equals(obj2) == true๋ผ๋ฉด, ๋ฐ๋์ obj1.hashCode() == obj2.hashCode()์ฌ์ผ ํ๋ค.
- ํ์ง๋ง ๊ทธ ๋ฐ๋๋ ์ฑ๋ฆฝํ์ง ์๋๋ค.
- ์ฆ, obj1.hashCode() == obj2.hashCode()๋ผ๊ณ ํด์ equals() ๊ฒฐ๊ณผ๊ฐ true์ธ ๊ฒ์ ์๋๋ค.
- ์ด๊ฒ์ ํด์ ์ถฉ๋์ด๋ผ ํ๋ฉฐ, ์ฑ๋ฅ ์ ํ๋ฅผ ์ ๋ฐํ ์ ์์ผ๋ฏ๋ก ์ต๋ํ ํผํ๋ ๊ฒ์ด ์ข๋ค.
- ํ๋ก๊ทธ๋จ์ด ์คํ๋๋ ๋์, ๊ฐ์ ๊ฐ์ฒด์ ๋ํด ํญ์ ๋์ผํ ํด์์ฝ๋๋ฅผ ๋ฐํํด์ผ ํ๋ค.
- ๋จ, Object์ ๊ธฐ๋ณธ ๊ตฌํ์ ์คํํ ๋๋ง๋ค ๋ฌ๋ผ์ง ์๋ ์๋ค.
๐น equals()์ hashCode()์ Override
๐ธ equals() Override์ ํ์์ฑ
์๋์ ๊ฐ์ Employee ํด๋์ค๊ฐ ์๋ค๊ณ ๊ฐ์ ํ์.
public class Employee {
private Integer id;
private String firstname;
private String lastName;
private String department;
}
์ด ํด๋์ค์์ id๋๊ณ ์ ์๋ณ์๋ก ์ฌ์ฉ๋๋ค.
์ฆ, id ๊ฐ์ด ๊ฐ์ผ๋ฉด ๋
ผ๋ฆฌ์ ์ผ๋ก ๊ฐ์ ๊ฐ์ฒด๋ก ๊ฐ์ฃผ๋์ด์ผ ํ๋ค.
ํ์ง๋ง equals()๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ์ง ์์ผ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก Object ํด๋์ค์ equals()๋ฅผ ๋ฐ๋ฅด๊ฒ ๋๋ฉฐ, ์ด๋ ์ฐธ์กฐ(๋ฉ๋ชจ๋ฆฌ ์ฃผ์)๋ฅผ ๋น๊ตํ๊ฒ ๋๋ค.
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
System.out.println(e1.equals(e2)); // false (equals() ์ค๋ฒ๋ผ์ด๋ฉํ์ง ์์)
โ equals() ์ค๋ฒ๋ผ์ด๋ฉ ์์
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (getClass() != o.getClass()) return false;
Employee e = (Employee) o;
return this.id != null && this.id.equals(e.getId());
}
- == ๋์ .equals()๋ฅผ ์ฌ์ฉํ์ฌ Integer ๊ฐ ๋น๊ต
- null ์ฒดํฌ ๋ฐ ํด๋์ค ํ์ ์ฒดํฌ๋ก ์์ ์ฑ ํฅ์
๐ equals()๋ง ์ค๋ฒ๋ผ์ด๋ฉํ์ ๋์ ๋ฌธ์ ์
equals()๋ง ์ค๋ฒ๋ผ์ด๋ฉํ๊ณ hashCode()๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ์ง ์์ผ๋ฉด, HashSet, HashMap ๊ฐ์ ํด์ ๊ธฐ๋ฐ ์ปฌ๋ ์ ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
๋ ผ๋ฆฌ์ ์ผ๋ก ๊ฐ์ ๊ฐ์ฒด๋ผ๋ ์๋ ๋ค๋ฅธ ํด์์ฝ๋๋ฅผ ๊ฐ์ง๊ธฐ ๋๋ฌธ์, ๊ฐ์ ๊ฐ์ฒด๋ก ์ธ์๋์ง ์๊ณ ์ค๋ณต ์ ์ฅ๋ ์ ์๋ค.
Set<Employee> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size()); // 2 (๋
ผ๋ฆฌ์ ์ผ๋ก ๊ฐ์ง๋ง ๋ค๋ฅธ ๊ฐ์ฒด๋ก ์ธ์๋จ)
โ ๋ฐ๋ผ์ equals()๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ๋ฉด ๋ฐ๋์ hashCode()๋ ํจ๊ป ์ค๋ฒ๋ผ์ด๋ฉํด์ผ ํ๋ค.
๐ธ hashCode() Override์ ํ์์ฑ
์์ ์ค๋ช ํ๋ฏ์ด HashTable, HashSet, HashMap ๊ฐ์ ํด์ ๊ธฐ๋ฐ ์๋ฃ๊ตฌ์กฐ๋ ๊ฐ์ฒด๋ฅผ ์ ์ฅํ ๋ ๋ด๋ถ์ ์ผ๋ก hashCode() ๊ฐ์ ์ด์ฉํด ์ ์ฅ ์์น๋ฅผ ๊ฒฐ์ ํ๋ค.
๊ทธ๋ ๋ค๋ฉด equals()๋ง ์ค๋ฒ๋ผ์ด๋ํ Employee ํด๋์ค๋ฅผ HashSet์ ์ ์ฅํ๋ฉด ์ด๋ค ์ผ์ด ์๊ธธ๊น ?
import java.util.HashSet;
import java.util.Set;
public class EqualsTest {
public static void main(String[] args) {
Employee e1 = new Employee();
Employee e2 = new Employee();
e1.setId(100);
e2.setId(100);
// equals()๊ฐ ์ค๋ฒ๋ผ์ด๋ฉ๋์ด ์๋ค๋ฉด true ์ถ๋ ฅ๋จ
System.out.println(e1.equals(e2)); // true
Set<Employee> employees = new HashSet<>();
employees.add(e1);
employees.add(e2);
// equals()๋ true์ง๋ง hashCode()๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ์ง ์์ผ๋ฉด,
// ๋ ๊ฐ์ฒด์ ํด์๊ฐ์ด ๋ฌ๋ผ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๋ก ํ๋จ๋์ด ๋ ๋ค ์ ์ฅ๋จ
System.out.println(employees); // [e1, e2] — ๋ ๊ฐ์ฒด๊ฐ ๋ชจ๋ ์ถ๋ ฅ๋จ
}
}
์ ์ฝ๋์์ equals()๋ ์ค๋ฒ๋ผ์ด๋ฉ๋์ด ๋ ผ๋ฆฌ์ ์ผ๋ก ๊ฐ์ ๊ฐ์ฒด๋ก ํ๋จ๋์ง๋ง, hashCode()๋ ์ค๋ฒ๋ผ์ด๋ฉํ์ง ์์๊ธฐ ๋๋ฌธ์ Object์ ๊ธฐ๋ณธ ๊ตฌํ์ด ์ฌ์ฉ๋๋ค.
์ฆ, ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ก ๋ค๋ฅธ ํด์๊ฐ์ด ์์ฑ๋๋ฏ๋ก HashSet์ ๋ ๊ฐ์ฒด๋ฅผ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด๋ก ์ธ์ํ๊ณ ๋ชจ๋ ์ ์ฅํ๊ฒ ๋๋ค.
โ hashCode() ์ค๋ฒ๋ผ์ด๋ฉ ์์
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด hashCode()๋ ์๋์ฒ๋ผ ์ค๋ฒ๋ผ์ด๋ฉํด์ค์ผ ํ๋ค.
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + (id != null ? id.hashCode() : 0);
return result;
}
- id๊ฐ null์ผ ์ ์์ผ๋ฏ๋ก null ์ฒดํฌ ํฌํจ
- Integer๋ ๊ฐ์ฒด์ด๋ฏ๋ก hashCode() ํธ์ถ ๊ฐ๋ฅ
- PRIME ์์ ๊ณฑ์ ์ ํด์ ์ถฉ๋ ๊ฐ๋ฅ์ฑ์ ์ค์ด๊ธฐ ์ํ ์ผ๋ฐ์ ์ธ ํจํด
โ ๊ฒฐ๊ณผ
์ด์ equals()์ hashCode()๋ฅผ ๋ชจ๋ ์ค๋ฒ๋ผ์ด๋ฉํ๊ธฐ ๋๋ฌธ์, ๋์ผํ id๋ฅผ ๊ฐ์ง ๊ฐ์ฒด๋ HashSet์์๋ ์ค๋ณต ์์ด ํ ๋ฒ๋ง ์ ์ฅ๋๋ค.
System.out.println(employees); // ์ด์ ํ๋์ ๊ฐ์ฒด๋ง ์ถ๋ ฅ๋จ
๐น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ Override
Apache Commons Lang ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด HashCodeBuilder์ EqualsBuilder๋ฅผ ํตํด hashCode()์ equals() ๋ฉ์๋๋ฅผ ๋ ๊ฐ๊ฒฐํ๊ณ ์์ ํ๊ฒ ์ค๋ฒ๋ผ์ด๋ฉํ ์ ์๋ค
.
โ ์์ ์ฝ๋ : Apache Commons๋ฅผ ํ์ฉํ equals() & hashCode()
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Employee {
private Integer id;
private String firstname;
private String lastName;
private String department;
// Getter & Setter ์๋ต
@Override
public int hashCode() {
final int PRIME = 31;
// ์ง์ ID์ ๊ฒฝ์ฐ ID+1์ ์ฌ์ฉํ์ฌ ํด์ ์ถฉ๋ ๋ฐฉ์ง
return new HashCodeBuilder(getId() % 2 == 0 ? getId() + 1 : getId(), PRIME)
.toHashCode();
}
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (getClass() != o.getClass()) return false;
Employee e = (Employee) o;
// id๊ฐ ๊ฐ์ผ๋ฉด ๋
ผ๋ฆฌ์ ์ผ๋ก ๋์ผํ ๊ฐ์ฒด๋ก ํ๋จ
return new EqualsBuilder()
.append(getId(), e.getId())
.isEquals();
}
}
๐ธ equals() & hashCode() Override ์ ์ฃผ์์ฌํญ
- ๋ ๋ฉ์๋๋ ํญ์ ํจ๊ป ์ค๋ฒ๋ผ์ด๋ฉํด์ผ ํ๋ค.
- ๊ฐ์ ์์ฑ(id ๋ฑ)์ ๊ธฐ์ค์ผ๋ก ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ์ผ๊ด์ฑ์ด ์ ์ง๋๋ค.
- equals() ๊ฒฐ๊ณผ๊ฐ true์ด๋ฉด, hashCode()๋ ๋ฐ๋์ ๊ฐ์์ผ ํ๋ค.
- ๊ฐ์ฒด๊ฐ ๋ณํ์ง ์์๋ค๋ฉด equals()์ hashCode() ๊ฒฐ๊ณผ๋ ๋ณํ์ง ์์์ผ ํ๋ค.
๐ธ ORM ์ฌ์ฉ ์ Getter ์ฌ์ฉ ๊ถ์ฅ
- JPA ๊ฐ์ ORM ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ํฐํฐ์ ํ๋๋ Lazy Loading๋ ์ ์๋ค.
- ์ด๋ ํ๋๋ฅผ ์ง์ ์ ๊ทผํ๋ฉด ๊ฐ์ด ์์ง ์ด๊ธฐํ๋์ง ์์ ์๊ธฐ์น ์์ ๋์์ด ๋ฐ์ํ ์ ์๋ค.
- ๋ฐ๋ผ์ ๋น๊ต๋ ํด์์ฝ๋ ์์ฑ ์์๋ ์ง์ ์ ๊ทผ ๋์ getter ๋ฉ์๋ ์ฌ์ฉ์ด ๊ถ์ฅ๋๋ค.
์ฌ์ฉ๋ฐฉ์ | ์์ ์ฌ๋ถ | ์ค๋ช |
this.id | โ ์ํ | ๊ฐ์ด ๋ก๋ฉ๋์ง ์์ null์ผ ์ ์์ |
this.getId() | โ ์์ | ํธ์ถ ์ ORM์ด ๊ฐ์ ๋ก๋ฉํจ |
๐ reference
'Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Java] ORM, MyBatis์ JPA ์ฐจ์ด (0) | 2025.03.13 |
---|---|
[Java] static ๋ณ์, static ๋ฉ์๋ (0) | 2025.02.23 |
[Java] Arrays.sort(), Collections.sort() (0) | 2025.02.14 |
[Java] ์๋ฃํ(Data Type), ํ ๋ณํ (0) | 2025.02.01 |
[Java] ์ ๊ท ํํ์(Regular Expression, Regex) (0) | 2025.01.30 |