item 11 hyowon - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

equals๋ฅผ ์žฌ์ •์˜ํ•œ ํด๋ž˜์Šค ๋ชจ๋‘์—์„œ hashCode๋„ ์žฌ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด hashCode ์ผ๋ฐ˜ ๊ทœ์•ฝ์„ ์–ด๊ธฐ๊ฒŒ ๋˜์–ด HashMap, HashSet ๊ฐ™์€ ์ปฌ๋ ‰์…˜์˜ ์›์†Œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ๊ฒƒ์ด๋‹ค.

Object ๋ช…์„ธ์˜ ๊ทœ์•ฝ (Object.hashCode())

  1. equals ๋น„๊ต์— ์‚ฌ์šฉ๋˜๋Š” ์ •๋ณด๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ๊ทธ ๊ฐ์ฒด์˜ hashCode ๋ฉ”์„œ๋“œ๋Š” ํ•ญ์ƒ ๊ฐ™์€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ๋‹จ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹คํ–‰ํ•œ๋‹ค๋ฉด ์ด ๊ฐ’์ด ๋‹ฌ๋ผ์ ธ๋„ ๋œ๋‹ค.
  2. equals(Object)๊ฐ€ ๋‘ ๊ฐ์ฒด๋ฅผ ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค๋ฉด, ๋‘ ๊ฐ์ฒด์˜ hashCode ๋ฐ˜ํ™˜๊ฐ’์€ ๊ฐ™์•„์•ผ ํ•œ๋‹ค.
  3. equals(Object)๊ฐ€ ๋‘ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅด๋‹ค๊ณ  ํŒ๋‹จํ•ด๋„, ๋‘ ๊ฐ์ฒด์˜ hashCode ๋ฐ˜ํ™˜๊ฐ’์ด ๋‹ค๋ฅผ ํ•„์š”๋Š” ์—†๋‹ค.

hashCode ์žฌ์ •์˜๋ฅผ ์ž˜๋ชปํ–ˆ์„ ๋•Œ ํฌ๊ฒŒ ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ์กฐํ•ญ์€ ๋‘ ๋ฒˆ์งธ๋กœ, ๋…ผ๋ฆฌ์ ์œผ๋กœ ๊ฐ™์€ ๊ฐ์ฒด๋Š” ๊ฐ™์€ ํ•ด์‹œ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ํ•„์š”์— ๋”ฐ๋ผ equals๋Š” ๋ฌผ๋ฆฌ์ (๋™์ผ์„ฑ)์œผ๋กœ ๋‹ค๋ฅด์ง€๋งŒ ๋…ผ๋ฆฌ์ (๋™๋“ฑ์„ฑ)์œผ๋กœ ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ๊ฐ™๋‹ค๊ณ  ์žฌ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ Object์˜ ๊ธฐ๋ณธ hashCode ๋ฉ”์„œ๋“œ๋Š” ์ด ๋‘˜์ด ์ „ํ˜€ ๋‹ค๋ฅด๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ, ๊ทœ์•ฝ๊ณผ ๋‹ฌ๋ฆฌ ๋‹ค๋ฅธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

Map<PhoneNumber, String> m = new HashMap<>();
m.put(new PhoneNumber(031,707,1234), "์ œ๋‹ˆ");
m.get(new PhoneNumber(031,707,1234));           // null

์ข‹์€ hashCode๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์š”๋ น

  1. int ๋ณ€์ˆ˜ result๋ฅผ ์„ ์–ธํ•œ ํ›„ ๊ฐ’ c๋กœ ์ดˆ๊ธฐํ™”. (์ด ๋•Œ c๋Š” ํ•ด๋‹น ๊ฐ์ฒด์˜ ํ•ต์‹ฌ ํ•„๋“œ๋ฅผ 2.1 ๋ฐฉ์‹์œผ๋กœ ๊ณ„์‚ฐํ•œ hashCode์ด๋‹ค)
  2. ํ•ด๋‹น ๊ฐ์ฒด์˜ ๋‚˜๋จธ์ง€ ํ•ต์‹ฌ ํ•„๋“œ f ๊ฐ๊ฐ์— ๋Œ€ํ•ด ๋‹ค์Œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
    1. ํ•ด๋‹น ํ•„๋“œ์˜ ํ•ด์‹œ์ฝ”๋“œ c๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค.

      1. (๊ธฐ๋ณธํƒ€์ž…์ธ ๊ฒฝ์šฐ) ${Type}.hashCode(f) ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. Type์€ ํ•ด๋‹น ๊ธฐ๋ณธ ํƒ€์ž…์˜ ๋ฐ•์‹ฑ ํด๋ž˜์Šค์ด๋‹ค
      2. (์ฐธ์กฐํƒ€์ž…์ธ ๊ฒฝ์šฐ) ์ด ํด๋ž˜์Šค์˜ equals ๋ฉ”์„œ๋“œ๊ฐ€ ์ด ํ•„๋“œ์˜ equals๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•ด ๋น„๊ตํ•œ๋‹ค๋ฉด ๊ทธ๋ ‡๊ฒŒ ํ•œ๋‹ค. ๋‹จ, ๊ณ„์‚ฐ์ด ๋ณต์žกํ•˜๋‹ค๋ฉด ์ด ํ•„๋“œ์˜ ํ‘œ์ค€ํ˜•(canonical representation)์„ ๋งŒ๋“ค์–ด ๊ทธ ํ‘œ์ค€ํ˜•์˜ hashCode๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ํ•„๋“œ์˜ ๊ฐ’์ด null์ด๋ฉด 0์„ ์‚ฌ์šฉํ•œ๋‹ค.
      3. (๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ) ํ•ต์‹ฌ ์›์†Œ ๊ฐ๊ฐ์„ ๋ณ„๋„ ํ•„๋“œ์ฒ˜๋Ÿผ ๋‹ค๋ฃฌ๋‹ค. ๋ฐฐ์—ด์— ํ•ต์‹ฌ ์›์†Œ๊ฐ€ ํ•˜๋‚˜๋„ ์—†๋‹ค๋ฉด ์ƒ์ˆ˜ 0์„ ์‚ฌ์šฉํ•˜๊ณ , ๋ชจ๋“  ์›์†Œ๊ฐ€ ํ•ต์‹ฌ ์›์†Œ๋ผ๋ฉด Arrays.hashCode๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    2. ๋‹จ๊ณ„ 2.1์—์„œ ๊ณ„์‚ฐํ•œ ํ•ด์‹œ์ฝ”๋“œ c๋กœ result๋ฅผ ๊ฐฑ์‹ ํ•œ๋‹ค.

      result = 31 * result + c;
      
    3. result๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

class PhoneNumber {
		private int areaCode;
    private String exchangeNumber;
    private int[] number;

    PhoneNumber(int areaCode, String exchangeNumber, int[] number) {
        this.areaCode = areaCode;
        this.exchangeNumber = exchangeNumber;
        this.number = number;
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(areaCode, exchangeNumber);
        result = 31 * result + Arrays.hashCode(number);
        return result;
    }
		
		@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PhoneNumber that = (PhoneNumber) o;
        return areaCode == that.areaCode &&
                Objects.equals(exchangeNumber, that.exchangeNumber) &&
                Arrays.equals(number, that.number);
    }
}

public class ItemEleven {
    public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<>();

        PhoneNumber pn = new PhoneNumber(031,707,1234);
        m.put(pn, "์ œ๋‹ˆ");
        m.get(new PhoneNumber(031,707,1234));

        // hashcode test
        System.out.println(m.get(new PhoneNumber(031,707,1234))); // ์ œ๋‹ˆ
        System.out.println(m.get(pn)); // ์ œ๋‹ˆ

        System.out.println(new PhoneNumber(031,707,1234).hashCode()); // 1247369920
        System.out.println(new PhoneNumber(031,707,1234).hashCode()); // 1247369920

    }
}

  • ํ•ด์‹œ ์ถฉ๋Œ์ด ๋”์šฑ ์ ์€ ๋ฐฉ๋ฒ•์„ ์›ํ•œ๋‹ค๋ฉด ๊ตฌ์•„๋ฐ”์˜ com.google.common.hash.Hashing์„ ์ฐธ๊ณ ํ•˜์ž.

  • Object ํด๋ž˜์Šค๋Š” ์ž„์˜ ๊ฐœ์ˆ˜์˜ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ ํ•ด์‹œ์ฝ”๋“œ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” hash ๋งค์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ๋‹จ, ์†๋„๋Š” ๋Š๋ ค์ง€๋Š”๋ฐ ์ž…๋ ฅ ์ธ์ˆ˜๋ฅผ ๋‹ด๊ธฐ ์œ„ํ•œ ๋ฐฐ์—ด์ด ๋งŒ๋“ค์–ด์ง€๊ณ , ์ž…๋ ฅ ์ค‘ ๊ธฐ๋ณธ ํƒ€์ž…์ด ์žˆ๋‹ค๋ฉด ๋ฐ•์‹ฑ, ์–ธ๋ฐ•์‹ฑ์ด ์ด๋ค„์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

  • ํด๋ž˜์Šค๊ฐ€ ๋ถˆ๋ณ€์ด๊ณ  ํ•ด์‹œ์ฝ”๋“œ๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๋น„์šฉ์ด ํฌ๋‹ค๋ฉด, ์บ์‹ฑํ•˜๋Š” ๋ฐฉ์‹์„ ๊ณ ๋ คํ•˜์ž

    • ์ด ํƒ€์ž…์˜ ๊ฐ์ฒด๊ฐ€ ์ฃผ๋กœ ํ•ด์‹œ์˜ ํ‚ค๋กœ ์‚ฌ์šฉ๋  ๊ฒƒ ๊ฐ™๋‹ค๋ฉด โ†’ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ์‹œ ํ•ด์‹œ์ฝ”๋“œ๋ฅผ ๊ณ„์‚ฐ
    • ์ž˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š์„ ๊ฒƒ ๊ฐ™๋‹ค๋ฉด โ†’ ์ง€์—ฐ ์ดˆ๊ธฐํ™” ์ „๋žต
    // ์ง€์—ฐ ์ดˆ๊ธฐํ™” ์ „๋žต
    private int hashCode; // ์ž๋™์œผ๋กœ 0์œผ๋กœ ์ดˆ๊ธฐํ™”
    @Override public int hashCode() {
    	int result = hashCode;
    	if (result == 0) {
    		result = Integer.hashCode(areaCode);
    		result = 31 * result + exchangeNumber.hashCode();
    		result = 31 * result + Arrays.hashCode();
    		hashCode = result;
    	}
    	return result;
    }
    
  • ์„ฑ๋Šฅ์„ ๋†’์ธ๋‹ค๊ณ  ํ•ต์‹ฌ ํ•„๋“œ๋ฅผ ์ƒ๋žตํ•œ๋‹ค๋ฉด ํ•ด์‹œ ํ’ˆ์งˆ์ด ๋‚˜๋น ์ ธ ๊ฐ™์€ ๊ฐ’์ด ๋Œ€๋Ÿ‰์œผ๋กœ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ๋‹ค.

  • hash ์ƒ์„ฑ๊ทœ์น™์„ api ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ณต๊ฐœํ•˜์ง€ ๋ง์ž. ๊ทธ๋ž˜์•ผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ด ๊ฐ’์— ์˜์ง€ํ•˜์ง€ ์•Š๊ณ  ์ถ”ํ›„ ๊ณ„์‚ฐ ๋ฐฉ์‹์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.