Как кэширующий хэш-код работает в Java, как это предложил Джошуа Блох в эффективной Java?
У меня есть следующий фрагмент кода из эффективной Java от Джошуа Блоха (пункт 9, глава 3, страница 49)
Если класс является неизменным и стоимость вычисления хеш-кода значительна, вы можете рассмотреть кэширование хеш-кода в объекте, а не пересчитывать его каждый раз, когда его запрашивают. Если вы считаете, что большинство объектов этого типа будут использоваться в качестве хеш-ключей, то вам следует вычислять хеш-код при создании экземпляра. В противном случае вы можете выбрать его ленивую инициализацию при первом вызове hashCode (элемент 71). Не ясно, что наш класс PhoneNumber заслуживает этой обработки, но просто чтобы показать вам, как это делается:
// 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 инициализирует ее 0
(JLS Раздел 4.12.5). Сравнение result == 0
на самом деле проверка, чтобы увидеть, если result
был назначен предположительно ненулевой хэш-код. Если он еще не был назначен, он выполняет вычисление, иначе он просто возвращает ранее вычисленный хэш-код.
Если бы вы действительно хотели, чтобы это работало правильно, вы бы поместили в переменную логическую переменную с именем 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;
}