Comments 15
OK, а в чём альтернативность-то? :) Про inconsistent hashing в разборе написано. А assert'ы лишь для того, чтобы показать ограничения на входные данные, не удлиняя сильно условие задачи.
На assert явным образом есть отсылка в авторском разборе:
У читателей может сложиться ложное впечатление, что это best practice, не так ли?
А вот тут
ничего нет про поведение
Вадим же человек аккуратный (вы посмотрите, сколько assertов!), значит, комментарий к методу он тоже написал аккуратно, а там указано, что метод вернет число в интервале от 0 до partitions исключительно.
У читателей может сложиться ложное впечатление, что это best practice, не так ли?
А вот тут
Другая, более серьезная проблема Вадима гораздо менее очевидна. Способ, который он выбрал, чтобы распределять данные по серверам, неконсистентен: при изменении количества партиций (например, при добавлении серверов) все треки перераспределяются по кластеру практически полностью.
ничего нет про поведение
Object.hashCode()
, а именно про его невоспроизводимость. Последнее, опять же на мой взгляд, куда большая проблема, чем изменение количество шардов, т.к. их количество может измениться либо вследствие сбоев, либо намеренного изменения (см. отсылки к Elasticsearch или MongoDB), в то время как hashCode
на разных серверах для одних и тех же объектов уже будет давать разные результаты. Это важно.hashCode у Integer и String — это не деталь реализации, а часть спецификации. Алгоритм явно прописан в Javadoc.
Строго говоря, ни одна из спецификаций (JLS, JVMS) это не гарантирует. Соглашусь с тем, что на практике проблема скорее всего надуманная.
Это гарантирует спецификация платформы Java SE 8/9/10.
JLS, JVMS, Javadoc,… — это части такой спецификации.
habrahabr.ru/company/mailru/blog/321306/#comment_10068620
JLS, JVMS, Javadoc,… — это части такой спецификации.
habrahabr.ru/company/mailru/blog/321306/#comment_10068620
Наверное, я некорректно выразился: нет гарантии совместимости Java 8 SE API и Java X SE API, т.е. описанное в javadoc поведение может поменяться в будущем. Или есть?
Точно так же, как и нет гарантий совместимости JVMS 8 и JVMS X. Могут, к примеру, пару байткодов удалить (как уже было с jsr/ret). Гарантии Javadoc в этом смысле не слабее и не сильнее гарантий других спецификаций.
Могут, к примеру, пару байткодов удалить (как уже было с jsr/ret).
Их не удалили, а всего лишь запретили использовать начиная с определённой версии class-файла:
If the class file version number is 51.0 or above, then neither thejsr
opcode or thejsr_w
opcode may appear in the code array
JVMS9 §4.9.1
И в JVMS9 они по-прежнему на месте (§jsr, §ret), и в class-файлах версии 50.0 и ниже они всё так же могут быть использованы.
Пример без проблем был запущен под OpenJDK 64-Bit Server VM 18.3 (build 10+46, mixed mode)
.
Спасибо, я в курсе, что HotSpot их поддерживает для старых классов (хотя и не обязан). Суть в том, что с API ровно та же история. Его теоретически могут менять в новых версиях, но на практике предусматривают решения для обратной совместимости. В некоторых случаях даже баги оставляют для поддержки предыдущих версий (см. свойства sun.nio.ch.bugLevel и sun.nio.cs.bugLevel).
Отлично, мы убедились, что результат воспроизводим между перезапусками приложений. Проблема же в том, что мы полагаемся на детали реализации. Нет никаких гарантий, что поведение останется неизменным в будущих версиях Java. Даже несмотря на однозначность javadoc'ов, которым мы доверяем, ведь они касаются только текущей реализации.
Этот javadoc документирует данную реализацию, поэтому код вполне может полагаться на неё.
В случае с java.lang.String
реализация hashCode()
не может взять и поменяться — от неё зависит, например, switch по строкам в уже скомпилированном коде.
Верное замечание, спасибо.
Такой исходный код:
компилируется в
Такой исходный код:
switch(s) {
case "s":
System.out.println("S");
break;
case "b":
System.out.println("B");
break;
}
компилируется в
int var_ = -1;
switch(s.hashCode()) {
case 98:
if(s.equals("b")) {
var_ = 1;
}
break;
case 115:
if(s.equals("s")) {
var_ = 0;
}
}
switch(var_) {
case 0:
System.out.println("S");
break;
case 1:
System.out.println("B");
}
Кстати, вместо варианта из оригинальной статьи:
я бы предложил такой вариант с обнулением бита, отвечающего за знак:
Для отрицательных значений
тем не менее, получим такое же распределение на
return Math.abs( track.hashCode() % partitions );
я бы предложил такой вариант с обнулением бита, отвечающего за знак:
return (track.hashCode() & 0x7FFFFFFF) % partitions;
Для отрицательных значений
hashCode
результат, конечно, будет отличаться:(-100 & 0x7FFFFFFF) % 7 // 0
Math.abs(-100 % 7) // 2
тем не менее, получим такое же распределение на
[0; partitions)
.Sign up to leave a comment.
Альтернативный взгляд на задачу от Одноклассников с JPoint 2018