>  기사  >  Java  >  equals() 메소드 및 hashCode() 메소드(자세한 소개)

equals() 메소드 및 hashCode() 메소드(자세한 소개)

青灯夜游
青灯夜游원래의
2019-11-23 15:18:372611검색

Java의 기본 클래스인 Object는 몇 가지 메소드를 제공하는데, 그 중 equals() 메소드는 두 객체가 동일한지 확인하는 데 사용되며, hashCode() 메소드는 객체의 해시 코드를 계산하는 데 사용됩니다. equals()나 hashCode()는 최종 메서드가 아니므로 덮어쓸 수 없습니다.

equals() 메소드 및 hashCode() 메소드(자세한 소개)

이 글에서는 두 메서드를 사용하고 다시 작성할 때 주의가 필요한 몇 가지 문제를 소개합니다.

1. equals() 메서드

Object 클래스의 equals() 메서드는 다음과 같이 구현됩니다. 다음: #🎜 🎜#

public boolean equals(Object obj) {
    return (this == obj);
}

이 구현에서 Object 클래스의 구현이 가장 높은 수준의 차별화를 갖춘 알고리즘을 채택한다는 것을 알 수 있습니다. 즉, 두 객체가 서로 일치하지 않는 한 동일한 객체인 경우 equals()는 false를 반환해야 합니다.

클래스를 정의할 때 equals() 메서드를 재정의할 수 있지만 JDK에서는 equals() 메서드를 구현할 때 따라야 하는 몇 가지 규칙을 설명합니다. #🎜 🎜 #

(1) 재귀성: x.equals(x)는 true를 반환해야 합니다.

(2) 대칭성: x.equals(y)와 y.equals(x)의 반환 값은 동일해야 합니다.

(3) 이행성: x.equals(y)가 true이고 y.equals(z)도 true인 경우 x.equals(z)도 true여야 합니다.

(4) 일관성: equals()의 x 및 y 객체가 사용하는 정보가 변경되지 않으면 x.equals(y) 값은 항상 변경되지 않습니다.

(5) Null이 아님: x는 null이 아니고 y는 null인 경우 x.equals(y)는 false여야 합니다.

2. hashCode() 메서드

1. ()Object 클래스의 hashCode() 메소드는 다음과 같이 선언됩니다.

public native int hashCode();

hashCode()가 다음과 같이 선언되어 있음을 알 수 있습니다. 기본 메서드이고 반환 값 유형은 정수입니다. 실제로 이 기본 메서드는 메모리에 있는 개체의 주소를 해시 코드로 반환하므로 서로 다른 개체의 반환 값이 서로 다르다는 것을 확인할 수 있습니다.

equals() 메서드와 유사하게 hashCode() 메서드를 재정의할 수 있습니다. JDK에서는 hashCode() 메소드의 기능과 구현 시 주의사항을 설명하고 있습니다.

(1) hashCode()는 java.util.HashMap과 같은 해시 테이블에서 작동합니다.

(2) equals()의 객체가 사용하는 정보가 변경되지 않은 경우 hashCode() 값은 항상 변경되지 않습니다.

(3) equals() 메서드를 사용하여 두 객체가 동일하다고 판단되면 hashCode() 메서드도 동일해야 합니다.

(4) equals() 메서드를 사용하여 두 객체가 동일하지 않다고 판단되는 경우 hashCode()는 필요하지 않으며 동일하지 않아야 합니다. 그러나 개발자는 동일하지 않은 객체가 서로 다른 hashCode를 생성하면 성능이 향상될 수 있다는 점을 인식해야 합니다. 해시 테이블의 성능.

2. hashCode()의 역할일반적으로 해싱에는 hashCode()가 사용됩니다. HashSet, HashMap 등과 같은 테이블의 함수

해시 테이블(HashSet, HashMap 등)에 객체를 추가할 때 먼저 hashCode() 메서드를 호출하여 해시 코드를 통해 객체의 해시 코드를 계산합니다. 해시 테이블에서 개체를 직접 찾을 수 있습니다. 해시 테이블의 위치(일반적으로 해시 테이블 크기에 대한 해시 코드 모듈로). 이 위치에 객체가 없으면 이 위치에 객체를 직접 삽입할 수 있습니다. 이 위치에 객체가 있으면(연결된 목록을 통해 구현된 객체가 두 개 이상 있을 수 있음), equals() 메서드를 호출하여 여부를 비교합니다. 이러한 객체는 객체와 동일합니다. 동일하면 객체를 저장할 필요가 없습니다. 동일하지 않으면 객체를 연결 목록에 추가합니다.

이것은 또한

equals()가 동일한 이유를 설명하며 hashCode()는 동일해야 합니다.

두 객체가 동일하면 해시 테이블(예: HashSet, HashMap 등)에 한 번만 나타나야 합니다. hashCode()가 동일하지 않으면 해시 테이블에 해시됩니다. 서로 다른 위치의 해시 테이블에 두 번 이상 나타납니다. 사실 JVM에서 로드된 객체는 메모리에 객체 헤더, 인스턴스 데이터, 채우기라는 세 부분을 포함합니다. 그 중 객체 헤더에는 객체의 타입에 대한 포인터와 MarkWord가 포함되어 있으며

MarkWord에는 객체의 GC 생성 연령 정보와 잠금 상태 정보뿐만 아니라 객체의 해시코드

도 포함됩니다. 객체 인스턴스 데이터는 객체에 의해 실제로 저장되는 유효한 정보입니다. HotSpot에서는 객체의 시작 주소가 8바이트의 정수 배수여야 하기 때문에 패딩 부분은 자리 표시자 역할만 합니다.

3. String 클래스의 equals() 및 hashCode() 구현String 클래스의 관련 구현 코드는 다음과 같습니다:

    private final char value[];
    private int hash; // Default to 0
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

코드를 통해 다음 사항을 확인할 수 있습니다.

1. 문자열 데이터는 최종적입니다. 즉, 문자열 개체가 생성되면, 예를 들어 String s = "hello"; s = "world";라는 문이 실행되면 문자열 개체의 값이 "world"로 변경되지 않습니다. , 그러나 새 String 개체가 생성되고 s 참조는 새 개체를 가리킵니다.

2. String 클래스는 성능 향상을 위해 hashCode()의 결과를 해시 값으로 캐시합니다.

3. String 객체가 동일하다는 조건은 둘 다 String 객체이고 길이가 동일하며 정확히 동일한 문자열 값을 가져야 한다는 것입니다. .

4. 문자열의 hashCode() 계산 공식은 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s [n-1]

hashCode()의 계산 과정에서 숫자 31이 사용되는 이유는 주요 이유는 다음과 같습니다.

1、使用质数计算哈希码,由于质数的特性,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小。

2、使用的质数越大,哈希冲突的概率越小,但是计算的速度也越慢;31是哈希冲突和性能的折中,实际上是实验观测的结果。

3、JVM会自动对31进行优化:31 * i == (i 77c98ee81678acf12c851e5747f5e864>> 32))

 (4) 如果是float值,则计算Float.floatToIntBits(f)

 (5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int

 (6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0

 (7) 如果是数组,那么需要为每个元素当做单独的域来处理。java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上。 

C、最后,把每个域的散列码合并到对象的哈希码中。

下面通过一个例子进行说明。在该例中,Person类重写了equals()方法和hashCode()方法。因为equals()方法中只使用了name域和age域,所以hashCode()方法中,也只计算name域和age域。

对于String类型的name域,直接使用了String的hashCode()方法;对于int类型的age域,直接用其值作为该域的hash。

public class Person {
	private String name;
	private int age;
	private boolean gender;

	public Person() {
		super();
	}

	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 boolean isGender() {
		return gender;
	}
	public void setGender(boolean gender) {
		this.gender = gender;
	}

	@Override
	public boolean equals(Object another) {
		if (this == another) {
			return true;
		}
		if (another instanceof Person) {
			Person anotherPerson = (Person) another;
			if (this.getName().equals(anotherPerson.getName()) && this.getAge() == anotherPerson.getAge()) {
				return true;
			} else {
				return false;
			}
		}
		return false;
	}

	@Override
	public int hashCode() {
		int hash = 17;
		hash = hash * 31 + getName().hashCode();
		hash = hash * 31 + getAge();
		return hash;
	}
}

推荐教程:java教程

위 내용은 equals() 메소드 및 hashCode() 메소드(자세한 소개)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.