Тут относительно недавно пробегали две статьи, в которых авторы затрагивали тему анализа хеш-функций с целью определить, какая лучше: Заметки о реализации hashCode() в Java и Changes to String internal representation made in Java 1.7.0_06 (перевод). В обеих статьях анализ проводился очень простой: генерировалось большое множество объектов нужного типа, для них вычислялись хеш-коды и среди хеш-кодов искалось количество коллизий (совпадений).
Я считаю, что такой анализ недостаточно хорош. Главное применение хеш-функций в Java — это в основанных на хешировании коллекциях, куда объекты складываются, распределяясь по ячейкам (бакетам, bucket) согласно своим хеш-кодам. Количество ячеек ограниченно, поэтому чтобы определить, в какой из них место объекту, берётся остаток от деления хеш-кода этого объекта на количество ячеек, которое из соображений производительности выбирается из степеней двойки. Так вот, равномерность распределения объектов по бакетам играет решающую роль в производительности коллекции, а значит очень важно, помимо количества коллизий, анализировать и её.
Легко показать, что малое число коллизий хеш-функции не всегда коррелирует с равномерным распределением объектов внутри хеш-таблицы. Достаточно взять любую хорошую хеш-функцию f(x) и получить из неё новую: h(x)=2*f(x). Количество коллизий у новой функции будет ровно таким же (для простоты забудем про переполнение), но при этом все её значения будут чётными, и распределение получится очень неравномерным.