안녕하세요.

오늘은 자바의 자료형과 == 연산자, equals(), hashCode() 에 대해 정리해보겠습니다.

 

 

Java Data Type

 

 

 

기본 자료형

 

  • 자바 컴파일러에 의해 해석되는 자료형
  • 실제 값을 갖는 자료형
  • 자바에서 여러 형태의 타입을 미리 정의하여 제공

 

 

참조 자료형

 

  • 값이 아닌 객체의 주소를 가지고 있는 자료형

 

 

 

== 연산자

 

피연산자가 primitive type일 때는 값이 같은지 비교하고, reference type일 때는 가리키는 주소가 같은지 검사한다.

아래 그림을 보면 "값이 같은지, 주소가 같은지 검사"한다는 것이 결국 해당 변수의 값을 비교하는 것임을 알 수 있다.

 

https://brunch.co.kr/@mystoryg/132

 

 

equals()

 

public static boolean equals(byte[] value, byte[] other) {
        if (value.length == other.length) {
            int len = value.length >> 1;
            for (int i = 0; i < len; i++) {
                if (getChar(value, i) != getChar(other, i)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
String str = null;
str.equals("");	// NullPointError
"".equals(str);	// Ok

 

문자열을 char 단위로 한 글자씩 비교하여 내용이 같은지 검사하는 메서드

String이 아닌 개발자가 생성한 클래스의 객체는 위 코드만으로는 내용이 같은지 판단할 수 없다.

 

equals() 메서드는 자바 최상위 클래스인 Object에 포함되어 있기 때문에 모든 하위 클래스에서 재정의하여 사용할 수 있다.

객체 내부에 equals() 메서드를 오버라이드하여 사용하도록 하자.

 

 

예)

public class Person {
    private String name;
    private int age;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass()) // 현재 참조하고 있는 클래스 확인
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

}

 

이 때, equals()만 정의해서는 안되고 반드시 hashCode()를 함께 오버라이드 해야한다.

hashCode: 객체의 주소값을 변환하여 생성한 객체의 고유한 정수 값

 

Set<Person> hset = new HashSet<>();
Person person1 = new Person("lyn", 28);
Person person2 = new Person("lyn", 28);
System.out.println("person1 : "+person1.hashCode());//2018699554
System.out.println("person2 : "+person2.hashCode());//1311053135
System.out.println(person1.equals(person2));//true
hset.add(person1);
hset.add(person2);
System.out.println(hset.size());//2


출처: https://jeong-pro.tistory.com/172 [기본기를 쌓는 정아마추어 코딩블로그]

 

person1과 person2의 해시코드가 다른 것을 확인할 수 있다.

따라서 person1과 person2 객체의 내용이 같음에도 불구하고 중복을 자동으로 없애주는 set에 넣었을 때 set의 size는 2가 된다.

 

equals()로 같은 객체라면 반드시 hashCode()도 같은 값을 가져야한다는 것

주의: hashCode()가 같은 값이어도 equals()로 같은 객체가 아닐 수도 있다.

 

 

hashCode()

 

public static int hashCode(byte[] value) {
        int h = 0;
        int length = value.length >> 1;
        for (int i = 0; i < length; i++) {
            h = 31 * h + getChar(value, i);
        }
        return h;
    }
    
    
static char getChar(byte[] val, int index) {
        assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
        index <<= 1;
        return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
                      ((val[index]   & 0xff) << LO_BYTE_SHIFT));
    }

 

문자열에서 char형으로 한 글자씩 가져와서 ascii 코드 값을 리턴하는 메서드

각 아스키코드 값을 더해서 해시 값으로 리턴하고 있다.

 

참고)

31을 곱하는 이유는 31*i 가 (i<<5) - i 와 같기 때문에 곱셈 대신 비트 이동 및 뺄셈으로 처리하여 성능면에서 더 이득이라고 한다.

하지만 요즘 VM은 자동으로 최적화 된다고..

 

String은 자바에서 byte(unsigned char)의 배열 형식이므로

String a = "hello"; 는

a[0] = 'h', a[1] = 'e', a[2] = 'l', a[3] = 'l' ... 으로 이루어져 있다.

 

h는 아스키 코드로 정수 104, e는 101

이것을 hash로 나열하게 되면 "s[0]*31^(n - 1) + s[1]*31^(n - 2) + ... + s[n - 1]"의 형태로 계산되어 hashCode가 만들어진다.

 

https://www.tutorialspoint.com/java/java_string_hashcode.htm

 

문자열의 각각의 Byte의 Ascii Code 공식을 더해 총합을 hash 값으로 사용하기 때문에 다른 문자열이라도 총 합이 같다면 같은 hash값을 가질 수 있는 것이다.

 

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + age;
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}


출처: https://jeong-pro.tistory.com/172 [기본기를 쌓는 정아마추어 코딩블로그]
복사했습니다!