해시코드 및 Equals 디버깅, 성능

몇 주 전에 나는 this story on reddit를 만났는데 그 중에서 지도에서 URL 종류를 키로 사용하는 문제를 토론했다.이것은 자바에서hashcode() 방법의 실현이 매우 느린 것으로 귀결된다.그물이 경우 클래스를 사용할 수 없는 URL입니다.불행하게도, 이것은 자바 API 규범의 일부분으로, 이전 호환성을 파괴하지 않는 상황에서 더 이상 복구할 수 있는 것이 아니다.
우리가 할 수 있는 일은 equals와hashcode의 문제를 이해하는 것이다.우리는 장래에 어떻게 이런 문제들을 피할 것인가?

URL 해시코드 및 Equals에 문제가 있습니까?
이 점을 이해하기 위해 우리는‌해시코드와 equals의 JavaDoc를 보십시오:

Compares this URL for equality with another object.

If the given object is not a URL then this method immediately returns false.

Two URL objects are equal if they have the same protocol, reference equivalent hosts, have the same port number on the host, and the same file and fragment of the file.

Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can't be resolved, the host names must be equal without regard to case; or both host names equal to null.

Since hosts comparison requires name resolution, this operation is a blocking operation.


호스트 비교에 이름 해석이 필요하기 때문에 이 동작은 차단 동작입니다.
아직 잘 모르겠습니다.간단한 코드 블록으로 설명하겠습니다.
System.out.println(new URL("http://localhost/").equals(new URL("http://127.0.0.1/")));
System.out.println(new URL("http://localhost/").hashCode() == new URL("http://127.0.0.1/").hashCode());
인쇄:
true
true
이것은 localhost에 있어서는 매우 간단할 수 있지만, 만약 우리가 비교역과 문자열이 같지 않다면, DNS 검색을 해야 한다.우리는hashcode() 호출만 하면 돼!

빠른 해결 방법
이 경우 URL을 피하는 것이 가장 빠른 해결책입니다.Sun은 클래스를 원래 JVM 코드에 깊이 포함하지만 대부분의 경우 URI를 사용할 수 있습니다.
예를 들어 해시코드와 equals 호출을 변경하고 URL 대신 URI를 사용하면 다음과 같은 결과를 얻을 수 있습니다.
System.out.println(new URI("http://localhost/").equals(new URI("http://127.0.0.1/")));
System.out.println(new URI("http://localhost/").hashCode() == new URI("http://127.0.0.1/").hashCode());
이 두 가지 견해는 모두 가짜다.일부 용례에서는 문제가 있을 수 있지만 성능에는 큰 차이가 있다.

더 큰 함정
만약 우리가 문자열만 맵키로 사용한다면, 괜찮을 것이다.우리가 이런 방법을 사용하는 곳마다 이런 실수가 우리를 습격할 것이다.
  • 세트
  • 지도
  • 스토리지
  • 비즈니스 논리
  • 하지만 더 깊어진다.우리가hashcode와equals 논리로 자신의 클래스를 작성할 때, 우리는 항상 나쁜 코드의 희생물이 된다.hashcode 방법의 작은 성능 손실이나 너무 간단한 버전은 추적하기 어려운 중대한 성능 손실을 초래할 수 있습니다.
    예를 들어hashcode 방법의 속도가 느리거나 정확하지 않기 때문에 더 많은 시간을 소비하는 흐름 조작은 장기적인 문제를 대표할 수 있다.

    최적 해시 코드 구현
    가장 좋은hashcode와equals 방법을 이해하기 위해서는 먼저 일반적인 코드를 이해해야 한다.지금 나는 무서운 코드나 낡은 코드를 보여주지 않을 것이다.이것은 좋은 코드이지만 가장 좋은 것은 아니다.
    public int hashCode() {
        return Objects.hash(id, core, setting, values, sets);
    }
    
    이 코드는 처음에는 괜찮아 보일 수도 있지만, 정말 될까요?
    다음은 이상적인 코드입니다.
    public int hashCode() {
        return id;
    }
    
    이것은 빠르고 100% 독특하며 정확하다.그 외에는 어떤 일도 할 이유가 없다.대상의 id에는 예외가 있습니다.이런 상황에서 우리는 대상이 되고 싶을 수도 있다.hashCode (id), null 등에도 적용됩니다.

    Hashcode는
    분명히 이것은 해시 코드를 작성할 때 기억해야 할 가장 중요한 일 중의 하나이다.이런 방법은 반드시 빨리 실행되어야 하며, 잘못된 상황에 대해서는 반드시 equals와 일치해야 한다.만약 사실이라면, 그것은 정확하지 않다.
    해시코드는 항상 다음 법률을 준수해야 합니다.
    assert(obj1.hashCode() != obj2.hashCode() && !obj1.equals(obj2));
    
    이것은hashcode 결과가 다르면 대상이 다르고 equals에서false로 되돌아와야 한다는 것을 의미한다.하지만 사실은 그렇지 않다.
    if(obj1.hashCode() == obj2.hashCode()) {
        if(obj1.equals(obj2)) {
           // this can be false...
        }
    }
    
    여기서 제공하는 가치는 성능에 있습니다.hashcode 방법의 집행 속도는 equals보다 훨씬 빠를 것이다.그것은 대가가 높을 수 있는 equals 계산과 색인 요소를 빠르게 뛰어넘을 수 있을 것이다.

    JPA 특수
    JPA 개발자는 대개 해시코드의 하드코딩 값만 사용하거나 Class 대상을 사용하여hashcode()를 생성합니다.이것은 네가 자세히 생각할 때까지 보기에 매우 이상하다.
    데이터베이스에 ID를 생성하도록 하면 객체가 저장되어 더 이상 소스 객체와 같지 않습니다. 솔루션은 주석과 데이터 유형을 사용하는 것입니다.하지만 데이터 모델을 바꿔야 한다.불행하게도 실체류는 적당한 해결 방법이 없다.
    사실 JPA 개발자가 Lombok을 사용할 때 겪는 많은 문제점은 해시 코드와 equals 방법을 만들어 주기 때문이라고 생각합니다.이것은 아마도 문제일 것이다.

    이것은 디버깅에 관한 블로그입니까?
    이렇게 오랫동안 설정해서 미안하지만, 그것은 매우 좋다.그래서 나는 더 일반적인 디버깅 방식으로 이 문제를 토론할 수 있는 모든 선언이 필요하다.이것은 유사한 범례를 사용하는 다른 언어에 있어서 정확하다는 것을 주의하십시오.
    이 블로그는 성능 문제부터 시작하여 디버깅의 측면에서 이 방면을 토론하고 싶다.많은 분석기에서, 해시 코드 방법의 비용은 거의 보이지 않는다.그러나 빈번하게 인용되고 영향이 넓기 때문에 결국 영향을 느끼고 책임을 다른 곳으로 미룰 수도 있다.
    무의식적인 반응은'가상'해시 코드 방법을 실현하고 이로 인해 발생하는 성능 차이를 살펴보는 것이다.유효한 숫자가 아니라 하드코딩된 숫자만 되돌려줍니다.
    이것은 어떤 경우에 매우 가치가 있으며, 심지어는 위에서 언급한hashcode 방법의 성능이 좋지 않은 문제를 해결할 수 있다.하지만 지도에는 도움이 되지 않는다.만약hashcode가 같은 값을 되돌려준다면, 맵의 키로 사용하면hashcode가 제공할 수 있는 모든 성능 우위를 효과적으로 비활성화할 수 있습니다.
    우리는hashcode 방법이 좋은지 아닌지를 어떻게 알 수 있습니까?
    우리는 디버거로 이 문제를 해결할 수 있다.지도를 보고 대상이 각 버킷 사이의 분포를 살펴보면hashcode 방법의 실제 가치를 알 수 있다.
    제출할 때 코드 검증 과정이 있다면,hashcode 방법의 복잡성 단계에서 규칙을 정의하는 것을 강력히 권장합니다.느린 코드가 스며들지 않도록 낮게 설정해야 한다.
    문제는 둥지를 짓는 것이다.예를 들어 우리가 전에 토론한 코드를 생각해 보자.
    public int hashCode() {
        return Objects.hash(id, core, setting, values, sets);
    }
    
    그것은 짧고 간단하다.그러나 이 코드의 성능은 어디에나 있을 수 있다.이 방법은 모든 내부 대상의hashcode 방법을 호출합니다.성능으로 말하자면, 이런 방법들은 아마 훨씬 떨어질 것이다.우리는 이것에 대해 경각심을 유지해야 한다.URL과 같은 JDK 클래스에 대해서도 앞에서 논의한 바와 같이 문제가 있다.

    TL;박사 01 명
    우리는 항상hashcode와equals 방법을 자동으로 생성합니다.IDE는 보통 이 점을 잘한다.그들은 우리가 비교하고자 하는 분야를 선택하기 위해 우리에게 선택을 제공했다.불행하게도, 이 두 필드는hashcode와 equals에 적용됩니다.
    때때로, 이것은 결코 중요하지 않다.보통 우리는 그것이 진정으로 중요한 부분을 볼 수 없다. 왜냐하면 이런 방법들은 너무 작아서 탐사기에 흔적을 남길 수 없기 때문이다.그러나 그것들은 광범위한 영향을 미치므로 우리는 이에 대해 최적화를 해야 한다.
    디버깅을 통해, 우리는 비추는 버킷의 분포를 검사하고 볼 수 있으며,hashcode 방법의 성능과 비추는 것과 유사한 API에서 더 일치하는 결과를 얻을 수 있도록 조정해야 하는지를 알 수 있습니다.

    좋은 웹페이지 즐겨찾기