equals๋ฅผ ์ฌ์ ์ํ ํด๋์ค ๋ชจ๋์์ hashCode๋ ์ฌ์ ์ํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด hashCode ์ผ๋ฐ ๊ท์ฝ์ ์ด๊ธฐ๊ฒ ๋์ด ํด๋น ํด๋์ค์ ์ธ์คํด์ค๋ฅผ HashMap์ด๋ HashSet ๊ฐ์ ์ปฌ๋ ์ ์ ์์๋ก ์ฌ์ฉํ ๋ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ๊ฒ์ด๋ค.
๐ hashCode ์ฌ์ ์ ์กฐ๊ฑด
- equals ๋น๊ต์ ์ฌ์ฉ๋๋ ์ ๋ณด๊ฐ ๋ณ๊ฒฝ๋์ง ์๋๋ค๋ฉด, hashCode๋ ๋ณํ๋ฉด ์๋๋ค.
- equals๊ฐ ๋ ๊ฐ์ฒด๊ฐ ๊ฐ๋ค๊ณ ํ๋จํ๋ค๋ฉด, ๋ ๊ฐ์ฒด์ hashCode๋ ๋๊ฐ์ ๊ฐ์ ๋ฐํํด์ผ ํ๋ค.
- equals๊ฐ ๋ ๊ฐ์ฒด๋ฅผ ๋ค๋ฅด๋ค๊ณ ํ๋จํ๋๋ผ๋, ๋ ๊ฐ์ฒด์ hashCode๊ฐ ๊ผญ ๋ค๋ฅผ ํ์๋ ์๋ค. ํ์ง๋ง ์ฑ๋ฅ์ ์ฑ๊ธฐ๋ ค๋ฉด hashCode๊ฐ์ด ๋ฌ๋ผ์ผ ํ๋ค.
equals(Object) ๋ฉ์๋๋ ๋ฌผ๋ฆฌ์ ์ผ๋ก๋ ๋ค๋ฅธ ๊ฐ์ฒด์ง๋ง, ๋
ผ๋ฆฌ์ ๋์น์ฑ์ ์ฑ๋ฆฝํ ๊ฒฝ์ฐ ๊ฐ๋ค๊ณ ์ฌ์ ์ ํ ์์๋ค.
๊ทธ๋ฐ๋ฐ ์ด ๊ฒฝ์ฐ, hashCode๊ฐ ์ฌ์ ์๋์ง ์๋๋ค๋ฉด, Object์ ๊ธฐ๋ณธ hashCode๊ฐ ์ํ๋๋๋ฐ ํด๋น ๋ฉ์๋์์๋ ๋
ผ๋ฆฌ์ ์ผ๋ก ๊ฐ๋ค๊ณ ํด๋ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋ค๋ฅด๋ค๊ณ ํ๋จ๋๋ฉด ์๋ก ๋ค๋ฅธ ๊ฐ์ ๋ฐํํ๋ค.
public class PhoneNumber {
private String prefix;
private String middle;
private String suffix;
...
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PhoneNumber)) return false;
PhoneNumber that = (PhoneNumber) o;
return Objects.equals(prefix, that.prefix)
&& Objects.equals(middle, that.middle)
&& Objects.equals(suffix, that.suffix);
}
public class PhoneNumberApplication {
public static void main(String[] args) {
PhoneNumber phoneNumber1 = new PhoneNumber("010", "1234", "5678");
PhoneNumber phoneNumber2 = new PhoneNumber("010", "1234", "5678");
System.out.println("phoneNumber1.equals(phoneNumber2) = " + phoneNumber1.equals(phoneNumber2));
Map<PhoneNumber, String> map = new HashMap<>();
map.put(phoneNumber1, "ramos");
System.out.println(map.get(phoneNumber1)); // ramos
System.out.println(map.get(phoneNumber2)); // null
}
}
}
equals๋ ์ฌ์ ์ํ๊ธฐ ๋๋ฌธ์ equals(Object)์ ๊ฒฐ๊ณผ๋ true์ด๋ค. ๋ ผ๋ฆฌ์ ์ผ๋ก ๋ณผ ๋, phoneNumber1๊ณผ phoneNumber2๋ ๋์ผํ๋ค๋ ์๋ฏธ์ด๋ค.
ํ์ง๋ง, HashMap์์ key๊ฐ์ผ๋ก phoneNumber1์ด ์๋ phoneNumber2๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ null์ ๋ฐํํ๋ค. ๊ทธ ์ด์ ๋ PhoneNumber ํด๋์ค๋ hashCode ๋ฉ์๋๋ฅผ ์ฌ์ ์ํ์ง ์์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ๋ ผ๋ฆฌ์ ์ผ๋ก ๋์ผํ๋๋ผ๋ ํด์์ฝ๋๋ ์๋ก ๋ค๋ฅด๊ฒ ๋ฐํ๋๊ธฐ์ ๋ ๋ฒ์งธ ๊ท์ฝ์ ์งํค์ง ๋ชปํ๋ค.
๋ฌธ์ ๋ ์์ง๋ง ๋ค์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์๋ ์๋๋ค.
@Override
public int hashCode() { return 42; }
hashCode๋ฅผ ์์ ๊ฐ์ด ์ฌ์ ์ํ๋ค๋ฉด, ๊ฐ์ ๊ฐ์ฒด๋ ๋ชจ๋ ๊ฐ์ ํด์์ฝ๋๋ฅผ ๋ฐํํ๋ค. ํ์ง๋ง, ๋ชจ๋ ๊ฐ์ฒด๊ฐ ๊ฐ์ ํด์์ฝ๋๋ฅผ ๋ฐํํ๊ธฐ์ ๋ชจ๋ ๊ฐ์ฒด๊ฐ ํด์ํ ์ด๋ธ ๋ฒํท ํ๋์ ๋ด๊ธฐ๊ธฐ ๋๋ฌธ์ ๋ง์น ์ฐ๊ฒฐ๋ฆฌ์คํธ์ฒ๋ผ ๋์ํ๊ฒ ๋๋ค.
ํ๊ท ์ํ ์๊ฐ์ด O(1)์ธ ํด์ํ ์ด๋ธ์ด O(n)์ผ๋ก ๋๋ ค์ ธ์, ๊ฐ์ฒด๊ฐ ๋ง์์ง๋ฉด ๋์ ํ ์ธ ์ ์๊ฒ ๋๋ค.
์ข์ hashCode ์์ฑ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
์ด์์ ์ธ ํด์ ํจ์๋ ์ฃผ์ด์ง ์ธ์คํด์ค๋ค์ 32bit ์ ์ ๋ฒ์์ ๊ท ์ผํ๊ฒ ๋ถ๋ฐฐํด์ผ ํ๋ค.
- ์ง์ญ๋ณ์ ์ ์ธ ํ ํต์ฌํ๋ ๊ฐ ํ๋์ ํด์์ฝ๋๋ก ์ด๊ธฐํ
- ๊ธฐ๋ณธํ์
ํ๋๋ผ๋ฉด
Type.hashCode(f)
๋ฅผ ์ํ (Type: Wrapper Class) - ์ฐธ์กฐํ์ ํ๋๋ผ๋ฉด ์ด ํ๋์ hashCode๋ฅผ ์ฌ๊ท์ ์ผ๋ก ํธ์ถํ๋ค. ๊ณ์ฐ์ด ๋ ๋ณต์กํด์ง ๊ฒ ๊ฐ์ผ๋ฉด, ์ด ํ๋์ ํ์คํ(canonical representation)์ ๋ง๋ค์ด ํ์คํ์ hashCode๋ฅผ ํธ์ถํ๋ค. ํ๋์ ๊ฐ์ด null์ด๋ฉด 0์ ์ฌ์ฉํ๋ค.
- ๋ฐฐ์ด์ด๋ผ๋ฉด ํต์ฌ ์์ ๊ฐ๊ฐ์ ๋ณ๋ ํ๋์ฒ๋ผ ๋ค๋ฃฌ๋ค. ๋ง์ฝ ๋ชจ๋ ์์๊ฐ ํต์ฌ ์์๋ผ๋ฉด
Arrays.hashCode
๋ฅผ ์ฌ์ฉํ๋ค.
- ๊ธฐ๋ณธํ์
ํ๋๋ผ๋ฉด
- ๋ค๋ฅธ ํต์ฌํ๋๋ค๋ ๋์ผํ๊ฒ ํด์์ฝ๋ํํ์ฌ ์ง์ญ๋ณ์์ ํฉ์น๋ค.
์ง์ญ๋ณ์ = 31 * ์ง์ญ๋ณ์ + ํต์ฌํ๋์ ํด์์ฝ๋
- ์ง์ญ๋ณ์์ ๊ฐ์ ๋ฐํํ๋ค.
@Override public int hashCode() { int result = prefix.hashCode(); result = 31 * result + middle.hashCode(); result = 31 * result + suffix.hashCode(); return result; }
์ฐธ๊ณ ๋ก, ์ง์ญ๋ณ์ = 31 * ์ง์ญ๋ณ์ + ํต์ฌํ๋์ ํด์์ฝ๋
์์ ๊ณฑํ ์ซ์๊ฐ 31์ธ ์ด์ ๋ 31์ด ํ์์ด๋ฉด์ ์์(prime)์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๋ง์ฝ ์ด ์ซ์๊ฐ ์ง์์ด๊ณ ์ค๋ฒํ๋ก๊ฐ ๋ฐ์ํ๋ค๋ฉด ์ ๋ณด๋ฅผ ์๊ฒ ๋๋ค. 2๋ฅผ ๊ณฑํ๋๊ฑด ์ํํธ ์ฐ์ฐ๊ณผ ๊ฐ๊ธฐ ๋๋ฌธ์ด๋ค. ์์๋ฅผ ๊ณฑํ๋ ์ด์ ๋ ์ ํต์ ์ผ๋ก ๊ทธ๋ฆฌ ํด์๋ค๊ณ ํ๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก 31์ ์ด์ฉํ๋ฉด, ์ด ๊ณฑ์
์ ์ํํธ ์ฐ์ฐ๊ณผ ๋บ์
์ผ๋ก ๋์ฒดํด ์ต์ ํํ ์ ์๋ค. ์์ฆ VM๋ค์ ์ด๋ฐ ์ต์ ํ๋ฅผ ์๋์ผ๋ก ํด์ค๋ค.
Objects ํด๋์ค๋ ์์์ ๊ฐฏ์๋งํผ ๊ฐ์ฒด๋ฅผ ๋ฐ์ ํด์์ฝ๋๋ฅผ ๊ณ์ฐํด์ฃผ๋ ์ ์ ๋ฉ์๋์ธ hash๋ฅผ ์ ๊ณตํ๋ค.
์ฅ์ ์ ์์ ๋น์ทํ ์์ค์ hashCode๋ฅผ ๋จ ํ ์ค๋ก ์์ฑ๊ฐ๋ฅํ๋ค.
๋จ์ ์ ์๋๊ฐ ๋ ๋๋ฆฌ๋ค. ์
๋ ฅ ์ธ์๋ฅผ ๋ด๊ธฐ ์ํ ๋ฐฐ์ด์ ๋ง๋ค๊ณ ์
๋ ฅ ์ค ๊ธฐ๋ณธ ํ์
์ด ์๋ค๋ฉด ๋ฐ์ฑ/์ธ๋ฐ์ฑ๋ ๊ฑฐ์ณ์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
@Override
public int hashCode() {
return Objects.hash(prefix, middle, suffix);
}
ํด๋์ค๊ฐ ๋ถ๋ณ์ด๊ณ ํด์์ฝ๋๋ฅผ ๊ณ์ฐํ๋ ๋น์ฉ์ด ํฌ๋ค๋ฉด, ๋งค๋ฒ ์๋ก ๊ณ์ฐํ๊ธฐ๋ณด๋ค๋ ์บ์ฑํ๋ ๋ฐฉ์์ ๊ณ ๋ คํด์ผ ํ๋ค. ๊ฐ์ฒด๊ฐ ์ฃผ๋ก ํด์์ ํค๋ก ์ฌ์ฉ๋ ๊ฒ ๊ฐ๋ค๋ฉด ์ธ์คํด์ค๊ฐ ๋ง๋ค์ด์ง ๋ ํด์์ฝ๋๋ฅผ ๊ณ์ฐํด๋ฌ์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ hashCode๋ฅผ ๋ฏธ๋ฆฌ ๊ณ์ฐํด๋๊ณ ์บ์ฑ๊น์ง ํด๋๋ ๊ฒ์ ๋น์ฉ๋ญ๋น์ด๋ค.
์ด๋ด ๊ฒฝ์ฐ, ์ง์ฐ ์ด๊ธฐํ(lazy initialization) ์ ๋ต์ ๊ณ ๋ คํด๋ณผ๋งํ๋ค.
private int hashCode; // ์๋์ผ๋ก 0์ผ๋ก ์ด๊ธฐํ
@Override
public int hashCode() {
int result = hashCode;
if (result == 0) {
result = Short.hashCode(areaCode);
result = 31 * result + Short.hashCode(prefix);
result = 31 * result + Short.hashCode(lineNum);
hashCode = result;
}
return result;
}
์ฑ๋ฅ์ ๋์ธ๋ต์๊ณ ํด์์ฝ๋๋ฅผ ๊ณ์ฐํ ๋ ํต์ฌ ํ๋๋ฅผ ์๋ตํด์๋ ์ ๋๋ค. ์๋๊ฐ ๋นจ๋ผ์ง์ง๋ง, ํด์ ํ์ง์ด ๋๋น ์ ธ ํด์ํ
์ด๋ธ์ ์ฑ๋ฅ์ ์ฌ๊ฐํ๊ฒ ๋จ์ด๋จ๋ฆด ์๋ ์๋ค.
ํนํ ์ด๋ค ํ๋๋ ํน์ ์์ญ์ ๋ชฐ๋ฆฐ ์ธ์คํด์ค๋ค์ ํด์์ฝ๋๋ฅผ ๋์ ๋ฒ์๋ก ๊ณ ๋ฅด๊ฒ ํผํธ๋ ค์ฃผ๋ ํจ๊ณผ๊ฐ ์์์ง๋ ๋ชจ๋ฅธ๋ค. ํํ ์ด๋ฐ ํ๋๋ฅผ ์๋ตํ๋ค๋ฉด ํด๋น ์์ญ์ ์ธ์คํด์ค๊ฐ ๋จ ๋ช ๊ฐ์ ํด์์ฝ๋๋ก ์ง์ค๋์ด ํด์ํ
์ด๋ธ์ ์๋๊ฐ ์ ํ์ผ๋ก ๋๋ ค์ง ๊ฒ์ด๋ค.
hashCode๊ฐ ๋ฐํํ๋ ๊ฐ์ ์์ฑ ๊ท์น์ API ์ฌ์ฉ์์๊ฒ ์์ธํ ๊ณตํํ์ง ๋ง์. ๊ทธ๋์ผ ํด๋ผ์ด์ธํธ๊ฐ ์ด ๊ฐ์ ์์งํ์ง ์๊ฒ ๋๊ณ , ์ถํ์ ๊ณ์ฐ ๋ฐฉ์์ ๋ฐ๊ฟ ์๋ ์๋ค.
- equals๋ฅผ ์ฌ์ ์ํ ๋๋ hashCode๋ ๋ฐ๋์ ์ฌ์ ์ํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ํ๋ก๊ทธ๋จ์ด ์ ๋๋ก ๋์ํ์ง ์์ ๊ฒ์ด๋ค.
- ์ฌ์ ์ํ hashCode๋ Object์ API ๋ฌธ์์ ๊ธฐ์ ๋ ์ผ๋ฐ ๊ท์ฝ์ ๋ฐ๋ผ์ผ ํ๋ฉฐ, ์๋ก ๋ค๋ฅธ ์ธ์คํด์ค๋ผ๋ฉด ๋๋๋ก ํด์์ฝ๋๋ ์๋ก ๋ค๋ฅด๊ฒ ๊ตฌํํด์ผ ํ๋ค.
- ์ด๋ฐ ๊ตฌํ ๋ฐฉ์์ด ๊ท์ฐฎ๋ค๋ฉด, AutoValue ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ์ง equals์ hashCode๋ฅผ ์๋์ผ๋ก ๋ง๋ค์ด์ค๋ค. IDE๋ค๋ ์ด๋ฐ ๊ธฐ๋ฅ์ ์ผ๋ถ ์ ๊ณตํ๋ค.