• ABOUT
  • PORTFOLIO
  • POSTS
  • GUESTBOOK

Β© 2025 BlueCool12 All rights reserved.

2025.10.26Java

πŸ”— μžλ°”μ˜ 객체 비ꡐ - equals()와 hashCode()λ₯Ό ν•¨κ»˜ μž¬μ •μ˜ν•΄μ•Ό ν•˜λŠ” 이유

1. equals() 

μžλ°”μ˜ equals() λ©”μ„œλ“œλŠ” 두 객체가 λ…Όλ¦¬μ μœΌλ‘œ 같은지 비ꡐ할 λ•Œ μ‚¬μš©ν•˜λŠ” λ©”μ„œλ“œμ΄λ‹€. μ΄λ•Œ β€œκ°™λ‹€β€μ˜ 기쀀은 λ‹¨μˆœνžˆ 같은 λ©”λͺ¨λ¦¬ μ£Όμ†Œλ₯Ό κ°€λ¦¬ν‚€λŠ”μ§€λ₯Ό μ˜λ―Έν•˜λŠ” 것이 μ•„λ‹Œ 객체의 λ‚΄λΆ€ 값이 λ™μΌν•œμ§€(논리적 동등성)λ₯Ό μ˜λ―Έν•œλ‹€. 

String a = new String("BlueCool");
String b = new String("BlueCool");

System.out.println(a == b); // false (참쑰값이 닀름)
System.out.println(a.equals(b)); // true (λ¬Έμžμ—΄μ΄ κ°™μŒ)

μœ„ μ˜ˆμ‹œμ—μ„œ == μ—°μ‚°μžλŠ” 두 객체가 같은 λ©”λͺ¨λ¦¬ μ£Όμ†Œ(μ°Έμ‘°)λ₯Ό κ°€μ§€λŠ”μ§€λ₯Ό λΉ„κ΅ν•œλ‹€. λ”°λΌμ„œ new ν‚€μ›Œλ“œλ‘œ 각각 μƒμ„±ν•œ String κ°μ²΄λŠ” μ„œλ‘œ λ‹€λ₯Έ λ©”λͺ¨λ¦¬ 곡간에 μ‘΄μž¬ν•˜λ―€λ‘œ λ‚΄μš©μ΄ 같더라도 falseλ₯Ό λ°˜ν™˜ν•œλ‹€. 

κ·ΈλŸ¬λ‚˜ String ν΄λž˜μŠ€λŠ” 이미 equals() λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜μ—¬ λ¬Έμžμ—΄μ˜ λ‚΄μš©μ΄ κ°™μœΌλ©΄ trueλ₯Ό λ°˜ν™˜ν•˜λ„λ‘ μž¬μ •μ˜λ˜μ–΄ μžˆλ‹€. 

public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}

즉 String ν΄λž˜μŠ€μ—μ„œλŠ” μ°Έμ‘° 동일성(==) λŒ€μ‹  논리적 동일성을 κΈ°μ€€μœΌλ‘œ 동등성을 νŒλ‹¨ν•œλ‹€. 

equals() λ©”μ„œλ“œλŠ” λͺ¨λ“  클래슀의 μ΅œμƒμœ„ λΆ€λͺ¨μΈ Object ν΄λž˜μŠ€μ— μ •μ˜λ˜μ–΄ 있으며 κΈ°λ³Έ κ΅¬ν˜„μ€ λ‹¨μˆœνžˆ 참쑰값이 같은지λ₯Ό λΉ„κ΅ν•œλ‹€. 

Object blue = new Object();
Object cool = new Object();

System.out.println(blue.equals(cool)); // false
System.out.println(blue == cool); // false

// μ‹€μ œ Object 클래슀의 equals() κΈ°λ³Έ κ΅¬ν˜„
public boolean equals(Object obj) {
return (this == obj);
}

λ”°λΌμ„œ μ‚¬μš©μžκ°€ 직접 μ •μ˜ν•œ ν΄λž˜μŠ€μ—μ„œ 객체의 논리적 동등성을 νŒλ‹¨ν•˜λ €λ©΄ λ‹€μŒμ˜ κ·œμΉ™μ„ μ§€ν‚€λ©° equals() λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•΄μ•Ό ν•œλ‹€. 

  • λ°˜μ‚¬μ„± 
    x.equals(x)λŠ” 항상 trueμ—¬μ•Ό ν•œλ‹€.
  • λŒ€μΉ­μ„± 
    x.equals(y)κ°€ trueλ©΄ y.equals(x)도 trueμ—¬μ•Ό ν•œλ‹€.
  • 좔이성 
    x.equals(y)와 y.equals(z)κ°€ trueλ©΄ x.equals(z)도 trueμ—¬μ•Ό ν•œλ‹€.
  • 일관성 
    비ꡐ λŒ€μƒμ΄ λ³€ν•˜μ§€ μ•ŠμœΌλ©΄ equals()의 κ²°κ³ΌλŠ” 항상 동일해야 ν•œλ‹€.
  • null μ•ˆμ •μ„± 
    μ–΄λ–€ 객체 x에 λŒ€ν•΄μ„œλ„ x.equals(null)은 항상 falseλ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•œλ‹€.


2. hashCode() 

hashCode()λŠ” 객체λ₯Ό 숫자둜 ν‘œν˜„ν•œ ν•΄μ‹œκ°’μ„ λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œμ΄λ‹€. 이 값은 HashMap, HashSetκ³Ό 같은 ν•΄μ‹œ 기반 μ»¬λ ‰μ…˜μ—μ„œ 객체λ₯Ό λΉ λ₯΄κ²Œ μ €μž₯ν•˜κ³  νƒμƒ‰ν•˜κΈ° μœ„ν•œ 버킷 인덱슀 ν‚€λ‘œ μ‚¬μš©λœλ‹€. 

Developer dev = new Developer("BlueCool", 12); 
int hash = dev.hashCode(); // μ˜ˆμ‹œ: 427492637

hashCode()의 역할은 λ‹¨μˆœνžˆ 숫자λ₯Ό λ§Œλ“œλŠ” 것이 μ•„λ‹ˆλΌ λ™λ“±ν•œ κ°μ²΄λŠ” λ°˜λ“œμ‹œ λ™μΌν•œ ν•΄μ‹œμ½”λ“œλ₯Ό κ°€μ Έμ•Ό ν•œλ‹€λŠ” κ·œμ•½μ„ μ§€ν‚€λŠ”λ° μžˆλ‹€. 

Hash 기반 μ»¬λ ‰μ…˜μ€ λ‚΄λΆ€μ μœΌλ‘œ hashCode() 값을 μ΄μš©ν•΄ μ €μž₯ μœ„μΉ˜(버킷)을 κ³„μ‚°ν•˜κ³  같은 버킷에 μ—¬λŸ¬ 객체가 μ‘΄μž¬ν•˜λŠ” 경우 equals() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ λ…Όλ¦¬μ μœΌλ‘œ 같은 객체인지 μ΅œμ’… ν™•μΈν•œλ‹€. 

λ”°λΌμ„œ equals()만 μž¬μ •μ˜ν•˜κ³  hashCode()λ₯Ό μž¬μ •μ˜ν•˜μ§€ μ•ŠμœΌλ©΄ λ…Όλ¦¬μ μœΌλ‘œ 같은 객체이더라도 μ„œλ‘œ λ‹€λ₯Έ ν•΄μ‹œκ°’μ„ κ°€μ§ˆ 수 있게 λ˜μ–΄ HashMap, HashSet λ“±μ˜ μ»¬λ ‰μ…˜μ—μ„œ μ€‘λ³΅μœΌλ‘œ μ €μž₯λ˜κ±°λ‚˜ 쑰회, μ‚­μ œμ— μ‹€νŒ¨ν•˜κ²Œ λ˜λŠ” λ“± 치λͺ…적인 였λ₯˜κ°€ λ°œμƒν•œλ‹€. 

λ”°λΌμ„œ equals()λ₯Ό μž¬μ •μ˜ν•  λ•ŒλŠ” λ°˜λ“œμ‹œ hashCode()도 ν•¨κ»˜ μž¬μ •μ˜ν•΄μ•Όν•˜λ©° hashCode λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ ν•  λ•ŒλŠ” λ‹€μŒκ³Ό 같은 κ·œμ•½μ„ λ”°λ₯Έλ‹€. 

  • 일관성 
    객체의 μƒνƒœκ°€ λ³€ν•˜μ§€ μ•ŠμœΌλ©΄ 같은 μ‹€ν–‰ μ€‘μ—λŠ” hashCode()의 값이 λ³€ν•˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€.
  • λ™μΉ˜ 동일 ν•΄μ‹œ 
    x.equals(y)κ°€ trueλ©΄ λ°˜λ“œμ‹œ x.hashCode() == y.hashCode()μ—¬μ•Ό ν•œλ‹€.
  • 좩돌 ν—ˆμš© 
    μ„œλ‘œ λ‹€λ₯Έ 객체라도 같은 ν•΄μ‹œμ½”λ“œλ₯Ό κ°€μ§ˆ 수 μžˆμ§€λ§Œ κ°€λŠ₯ν•œ μΆ©λŒμ„ μ€„μ΄λŠ” 것이 μ’‹λ‹€. 


3. equals()와 hashCode() κ΅¬ν˜„ μ˜ˆμ‹œ

μ‚¬μš©μž μ •μ˜ ν΄λž˜μŠ€μ—μ„œ 객체의 논리적 동등성을 νŒλ‹¨ν•˜λ €λ©΄ equals()와 hashCode()λ₯Ό ν•¨κ»˜ μž¬μ •μ˜ν•΄μ•Ό ν•œλ‹€. 일반적으둜 λ‹€μŒκ³Ό 같은 λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν•œλ‹€.

import java.util.Objects;

class Developer {

private String name;
private int age;

public Developer(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public boolean equals(Object o) {
if (this === o) return true;
if (!(o instanceof Developer)) return false;

Developer dev = (Developer) o;
return age == dev.age && Objects.equals(name, dev.name);
}

@Override
public int hashCode() {
return Objects.hash(name, age);
}
}

μœ„ μ½”λ“œμ—μ„œλŠ” Objects.equals()와 Objects.hash()λ₯Ό μ‚¬μš©ν•˜μ—¬ equals()와 hashCode()λ₯Ό κ°„κ²°ν•˜κ²Œ κ΅¬ν˜„ν•˜μ˜€λ‹€.

μ΄λ ‡κ²Œ κ΅¬ν˜„ν•˜λ©΄ 두 Developer 객체의 nameκ³Ό age 값이 λ™μΌν•œ 경우 같은 객체둜 νŒλ‹¨ν•˜λ©° λ™μΌν•œ ν•΄μ‹œμ½”λ“œλ₯Ό λ°˜ν™˜ν•˜κ²Œ λœλ‹€.

μ‹€λ¬΄μ—μ„œλŠ” Lombok을 μ‚¬μš©ν•˜μ—¬ equals()와 hashCode()λ₯Ό μžλ™μœΌλ‘œ μƒμ„±ν•˜λŠ” κ²½μš°λ„ λ§Žλ‹€.

import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class Developer {
private String name;
private int age;
}​

λ˜λŠ” @Data μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ equals(), hashCode(), getter, setter 등을 ν•¨κ»˜ μžλ™ 생성할 수 μžˆλ‹€.

이전 κΈ€
βš™οΈ 운영 ν™˜κ²½κΉŒμ§€ κ³ λ €ν•œ JPA μ„€μ • κ°€μ΄λ“œ
λ‹€μŒ κΈ€
πŸ”€ Java의 String이 νŠΉλ³„ν•œ 이유 - String의 핡심 νŠΉμ§• 3κ°€μ§€
μž₯μ‹μš© 둜κ³