Java equals()์™€ hashCode()์— ๋Œ€ํ•ด

equals()

  • ๋‘ ๊ฐ์ฒด์˜ ๋‚ด์šฉ์ด ๊ฐ™์€์ง€ ํ™•์ธํ•˜๋Š” Method์ž…๋‹ˆ๋‹ค.

hashCode()

  • ๋‘ ๊ฐ์ฒด๊ฐ€ ๊ฐ™์€ ๊ฐ์ฒด์ธ์ง€ ํ™•์ธํ•˜๋Š” Method์ž…๋‹ˆ๋‹ค.

Example

  • Nesoy๋ผ๋Š” ํด๋ž˜์Šค๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.
class Nesoy {
	private int age;
	private String name;
 
	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;
	}
}
  • Nesoy๋กœ ๋งŒ๋“ค์–ด์ง„ p1,p2๋ฅผ ๋™์ผํ•œ์ง€ ํ™•์ธํ–ˆ์ง€๋งŒ false ๊ฒฐ๊ณผ๊ฐ’์ด ๋‚˜์™”์Šต๋‹ˆ๋‹ค.
public static void main(String[] args){
	Nesoy p1 = new Nesoy();
	p1.setAge(27);
	p1.setName("YoungJae");
 
	Nesoy p2 = new Nesoy();
	p2.setAge(27);
	p2.setName("YoungJae");
 
	System.out.println(p1.equals(p2)); // false
}

์™œ(Why) ๊ฒฐ๊ณผ๊ฐ€ false์ผ๊นŒ์š”?

  • Object์— ์ •์˜๋œ equals๋ฅผ ํ™•์ธํ•˜๋ฉด ์ •๋‹ต์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋‹จ์ˆœํžˆ Object์˜ ==๋กœ ๋น„๊ตํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ(How) ๊ฐ’์ด ๋™์ผํ•œ์ง€ ๋น„๊ตํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

  • equals()๋ฅผ @Overrideํ•˜๋ฉด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
@Override
public boolean equals(Object obj) {
 
    if (obj == null) {
        return false;
    }
 
    if (this.getClass() != obj.getClass()) {
        return false;
    }
 
    if (this == obj) {
        System.out.println("Object Same");
        return true;
    }
 
    Nesoy that = (Nesoy) obj;
 
    if (this.name == null && that.name != null) {
        return false;
    }
 
    if (this.age == that.age && this.name.equals(that.name)) {
        System.out.println("Object Value Same");
        return true;
    }
 
    return false;
}

Result

์ด๋Ÿฐ ์ƒํ™ฉ์€ ์–ด๋–จ๊นŒ์š”?

  • equals()๊ฐ€ true์ธ ๋‘ Object๋ฅผ HashMap์— put์„ ํ•  ๋•Œ ๋™์ผํ•œ Key๋กœ ์ธ์‹ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ
Map<Nesoy, Integer> map = new HashMap<Nesoy, Integer>();
map.put(p1, 1);
map.put(p2, 1);
System.out.println(map.size()); // 2

์™œ(Why) ํฌ๊ธฐ๊ฐ€ 2์ผ๊นŒ์š”?

  • Hash๋ฅผ ์‚ฌ์šฉํ•œ Collection(HashMap, HashTable, HashSet, LinkedHashSet๋“ฑ๋“ฑ)์€ key๋ฅผ ๊ฒฐ์ •ํ• ๋•Œ hashCode()๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

์–ด๋–ป๊ฒŒ(How) Key ๊ฐ’์ด ๋™์ผํ•˜๊ฒŒ ์ธ์‹ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

  • hashCode()๋ฅผ @Overrideํ•˜๋ฉด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
@Override
public int hashCode() {
 
	final int prime = 31;
	int hashCode = 1;
 
	hashCode = prime * hashCode + ((name == null) ? 0 : name.hashCode());
	hashCode = prime * hashCode + age;
 
	return hashCode;
}

Result

์–ด๋–ป๊ฒŒ(How) hashCode()๋Š” ๊ฒฐ์ •๋˜๋Š”๊ฑธ๊นŒ์š”?

Object์˜ hashCode()

  • hashCode()๋กœ native call์„ ํ•˜์—ฌ Memory์—์„œ ๊ฐ€์ง„ ํ•ด์‰ฌ ์ฃผ์†Œ๊ฐ’์„ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  • ํŠน๋ณ„ํ•œ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ System.identityHashCode()์™€ ๋™์ผํ•œ ๊ฐ’์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

String์˜ hashCode()

์™œ(Why) 31์ธ๊ฐ€์š”?

31์€ ์†Œ์ˆ˜์ด๋ฉด์„œ ํ™€์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ ํƒ๋œ ๊ฐ’์ด๋‹ค. ๋งŒ์ผ ๊ทธ ๊ฐ’์ด ์ง์ˆ˜์˜€๊ณ  ๊ณฑ์…ˆ ๊ฒฐ๊ณผ๊ฐ€ ์˜ค๋ฒ„ํ”Œ๋กœ๋˜์—ˆ๋‹ค๋ฉด ์ •๋ณด๋Š” ์‚ฌ๋ผ์กŒ์„ ๊ฒƒ์ด๋‹ค. 2๋กœ ๊ณฑํ•˜๋Š” ๊ฒƒ์€ ๋น„ํŠธ๋ฅผ ์™ผ์ชฝ์œผ๋กœ shiftํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์†Œ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์ ์€ ๊ทธ๋‹ค์ง€ ๋ถ„๋ช…ํ•˜์ง€ ์•Š์ง€๋งŒ ์ „ํ†ต์ ์œผ๋กœ ๋„๋ฆฌ ์‚ฌ์šฉ๋œ๋‹ค. 31์˜ ์ข‹์€ ์ ์€ ๊ณฑ์…ˆ์„ ์‹œํ”„ํŠธ์™€ ๋บ„์…ˆ์˜ ์กฐํ•ฉ์œผ๋กœ ๋ฐ”๊พธ๋ฉด ๋” ์ข‹์€ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค(31 * i๋Š” (i << 5) - i ์™€ ๊ฐ™๋‹ค). ์ตœ์‹  VM์€ ์ด๋Ÿฐ ์ตœ์ ํ™”๋ฅผ ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค.

  • ์†Œ์ˆ˜์ด๋ฉด์„œ ํ™€์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ ํƒ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    • ์†Œ์ˆ˜๋Š” 1๊ณผ ์ž๊ธฐ ์ž์‹ ์„ ์ œ์™ธํ•œ ์ˆซ์ž์ด๊ธฐ ๋•Œ๋ฌธ์— Hashํ•˜์˜€์„ ๊ฒฝ์šฐ ์ถฉ๋Œ์ด ๊ฐ€์žฅ ์ ์€ ์ˆซ์ž์ž…๋‹ˆ๋‹ค.
    • ์–ธ์ œ๋“ ์ง€ ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ •๋ฆฌํ•˜๋ฉฐ

  • equals()๋ฅผ ์žฌ์ •์˜ํ•œ๋‹ค๋ฉด side effect๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด์„œ hashCode()๋„ ์žฌ์ •์˜ํ•˜๋Š”๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

Reference