Skip to content

Latest commit

ย 

History

History
136 lines (101 loc) ยท 7.72 KB

File metadata and controls

136 lines (101 loc) ยท 7.72 KB

์•„์ดํ…œ 11. equals๋ฅผ ์žฌ์ •์˜ํ•˜๋ ค๊ฑฐ๋“  hashCode๋„ ์žฌ์ •์˜ํ•˜๋ผ

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 ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ

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๋“ค๋„ ์ด๋Ÿฐ ๊ธฐ๋Šฅ์„ ์ผ๋ถ€ ์ œ๊ณตํ•œ๋‹ค.