안녕하세요.
오늘은 자바의 자료형과 == 연산자, equals(), hashCode() 에 대해 정리해보겠습니다.
Java Data Type
기본 자료형
- 자바 컴파일러에 의해 해석되는 자료형
- 실제 값을 갖는 자료형
- 자바에서 여러 형태의 타입을 미리 정의하여 제공
참조 자료형
- 값이 아닌 객체의 주소를 가지고 있는 자료형
== 연산자
피연산자가 primitive type일 때는 값이 같은지 비교하고, reference type일 때는 가리키는 주소가 같은지 검사한다.
아래 그림을 보면 "값이 같은지, 주소가 같은지 검사"한다는 것이 결국 해당 변수의 값을 비교하는 것임을 알 수 있다.
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 [기본기를 쌓는 정아마추어 코딩블로그]
'Java' 카테고리의 다른 글
[Java] Builder Design Pattern 빌더 패턴 (0) | 2022.02.20 |
---|---|
Java 17, 무엇이 바뀌었을까? (0) | 2021.10.25 |
[Java8] 자바 스트림 Java Stream API (0) | 2021.06.01 |
[Java8] 동작/행위 파라미터 (+람다식) (0) | 2021.05.25 |
[Java] 예외 처리 및 구분 Exception, throw, throws (1) | 2021.05.17 |