как кэширование хэш-кода работает в Java, как предложил Джошуа блох в эффективной java?

у меня есть следующий фрагмент кода из эффективной java Джошуа Блоха (пункт 9, Глава 3, стр. 49)

Если класс является неизменяемым и стоимость вычисления хэш-код важно, вы можете рассмотреть возможность кэширования хэш-кода в объекте вместо того, чтобы пересчитывать его каждый раз, когда он запрашивается. Если ты веришь ... что большинство объектов этого типа будут использоваться как хэш-ключи, то вы следует вычислить хэш-код при создании экземпляра. Иначе, можно лениво инициализировать его в первый раз вызывается хэш-код (пункт 71). Не ясно, что наш номер телефона класс заслуживает этого лечения, но просто чтобы показать вам, как это делается:

    // Lazily initialized, cached hashCode
    private volatile int hashCode;  // (See Item 71)
    @Override public int hashCode() {
        int result = hashCode;
        if (result == 0) {
            result = 17;
            result = 31 * result + areaCode;
            result = 31 * result + prefix;
            result = 31 * result + lineNumber;
            hashCode = result;
        }
        return result;
    }

мой вопрос в том, как кэширование (запоминание хэш-кода) работает здесь. В самый первый раз, hashCode() вызывается метод, нет hashCode назначить его результату. краткое объяснение того, как работает это кэширование, будет отличным. Спасибо

3 ответов


простой. Прочитайте мои встроенные комментарии ниже...

private volatile int hashCode;
//You keep a member field on the class, which represents the cached hashCode value

   @Override public int hashCode() {
       int result = hashCode;
       //if result == 0, the hashCode has not been computed yet, so compute it
       if (result == 0) {
           result = 17;
           result = 31 * result + areaCode;
           result = 31 * result + prefix;
           result = 31 * result + lineNumber;
           //remember the value you computed in the hashCode member field
           hashCode = result;
       }
       // when you return result, you've either just come from the body of the above
       // if statement, in which case you JUST calculated the value -- or -- you've
       // skipped the if statement in which case you've calculated it in a prior
       // invocation of hashCode, and you're returning the cached value.
       return result;
   }

на hashCode переменная в переменной экземпляра, и она не инициализируется явно,таким образом, Java intializes его в 0 (JLS раздел 4.12.5). Сравнение result == 0 фактически проверка, чтобы увидеть, если result был назначен предположительно ненулевой хэш-код. Если он еще не был назначен, то он выполняет вычисление, иначе он просто возвращает ранее вычисленный хэш-код.


Если бы вы действительно хотели, чтобы это работало правильно, вы бы поставили другую переменную volatile boolean под названием isHashInvalid. Каждый сеттер, включающий значения, доступные в вашей хэш-функции, будет устанавливать эту переменную. Затем он становится, (нет необходимости проверять " 0 " сейчас):

private volatile int isHashInvalid=TRUE;
private volatile int hashCode; //Automatically zero but it doesn't matter

//You keep a member field on the class, which represents the cached hashCode value
@Override public int hashCode() {
    int result = hashCode;
    if (isHashInvalid) {
       result = 17;
       result = 31 * result + areaCode;
       result = 31 * result + prefix;
       result = 31 * result + lineNumber;
       //remember the value you computed in the hashCode member field
       hashCode = result;
       isHashInvalid=FALSE;
    }
    // when you return result, you've either just come from the body of the above
    // if statement, in which case you JUST calculated the value -- or -- you've
    // skipped the if statement in which case you've calculated it in a prior
    // invocation of hashCode, and you're returning the cached value.
    return result;
}