Objects.hash() в hot path — рабочий код до профилирования
Objects.hash(a, b, c) читается как готовое решение для hashCode() — один вызов, никаких ручных умножений на 31. Компилируется, тесты зелёные, код-ревью пройдено.
Проблема в том, что метод принимает Object... — varargs. Каждый вызов упаковывает примитивы в обёртки, создаёт новый Object[] нужного размера, а затем прогоняет Arrays.hashCode() по этому массиву. Для класса, который лежит миллионами экземпляров в HashMap и вызывает hashCode() на каждый lookup, это лишняя аллокация на каждый вызов.
String эту проблему решил давно — кэширует вычисленный hash в поле после первого вызова, потому что String immutable и hash не может измениться. Для своего класса это тоже работает, если поля не меняются после создания.
private int hash;
@Override public int hashCode() { int h = hash; if (h == 0) { h = Objects.hash(id, type, version); hash = h; } return h; }
Следующий вопрос — почему нельзя просто кэшировать hash в мутабельном классе. Если поля меняются после того, как объект уже попал в HashMap как ключ, закэшированный hash разойдётся с actual — объект станет ненаходимым в своём же бакете. Кэш hashCode безопасен только для эффективно immutable объектов.
Objects.hash() — нормальный выбор для DTO, которые создаются и живут недолго. Для ключа в миллионном HashMap стоит посчитать, во что превращается один hashCode() под нагрузкой.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки