Ответ
Да, метод hashCode() может и часто возвращает отрицательные числа. Возвращаемый тип int имеет диапазон от -2^31 до 2^31-1. Отрицательный хэш-код является абсолютно валидным.
Примеры:
System.out.println("hello".hashCode()); // 99162322 (положительный)
System.out.println("Hello".hashCode()); // 69609650 (положительный)
System.out.println("password".hashCode()); // 488896668 (положительный)
System.out.println("z".hashCode()); // 122 (положительный)
// Многие переопределённые hashCode() могут давать отрицательные значения
System.out.println(new Object().hashCode()); // Может быть любым int
Как коллекции работают с отрицательными хэшами?
Классы вроде HashMap внутренне преобразуют хэш-код для вычисления индекса корзины (bucket). Они используют побитовые операции, которые корректно работают с отрицательными числами.
Важное предупреждение:
// НЕДОПУСТИМЫЙ подход для приведения к неотрицательному индексу
int index = Math.abs(obj.hashCode()) % tableSize;
// Проблема: Math.abs(Integer.MIN_VALUE) возвращает ОТРИЦАТЕЛЬНОЕ значение (переполнение).
// КОРРЕКТНЫЙ подход (аналогичный HashMap):
int hash = obj.hashCode();
int index = (hash & 0x7fffffff) % tableSize; // Обнуляем знаковый бит
// Или, что чаще:
int index = (table.length - 1) & hash; // Побитовое И с маской
Главное правило: Контракт hashCode() требует только консистентности в рамках одного выполнения приложения и согласованности с equals(). Знак значения не имеет значения.