Подтвердить что ты не робот

Почему метод equals в String не использует хэш?

Код метода equals в классе String равен

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = offset;
            int j = anotherString.offset;
            while (n-- != 0) {
                if (v1[i++] != v2[j++])
                    return false;
            }
            return true;
        }
    }
    return false;
}

У меня вопрос: почему этот метод не использует hashCode()?

Насколько я знаю, hashCode() может быстро сравнивать две строки.

ОБНОВЛЕНИЕ: Я знаю, что две неравные строки могут иметь одинаковые хэши. Но две равные строки имеют равные хэши. Итак, используя hashCode(), мы можем сразу увидеть, что две строки неравны.

Я просто думаю, что использование hashCode() может быть хорошим фильтром в equals.

ОБНОВЛЕНИЕ 2: Здесь некоторый код, о котором мы говорим здесь.

Это пример того, как метод String может выглядеть как

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        if (hashCode() == anotherString.hashCode()){
            int n = count;
            if (n == anotherString.count) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = offset;
                int j = anotherString.offset;
                while (n-- != 0) {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }
        }else{
            return false;
        }
    }
    return false;
}
4b9b3361

Ответ 1

Hashcode может быть проверкой первого раунда на неравенство. Однако он представляет некоторые компромиссы.

  • String хэш-коды лениво вычисляются, хотя они используют значение "охранник". Если вы сравниваете строки с длинными сроками жизни (то есть, скорее всего, они рассчитали хэш-код), это не проблема. В противном случае вы застряли в вычислении хэш-кода (потенциально дорого) или игнорировании проверки, когда хэш-код еще не был вычислен. Если у вас много короткоживущих строк, вы будете игнорировать чек чаще, чем будете использовать его.
  • В реальном мире большинство строк различаются по своим первым нескольким символам, поэтому вы не будете экономить много, сначала проверив hashcode. Есть, конечно, исключения (например, URL-адреса), но опять же, в реальном программировании они происходят нечасто.

Ответ 2

Этот вопрос фактически был рассмотрен разработчиками JDK. Я не мог найти в различные сообщения, почему он не был включен. Усовершенствование также включено в базу данных ошибок.

А именно, одно из предлагаемых изменений:

public boolean equals(Object anObject) {
    if (this == anObject) // 1st check identitiy
        return true;
    if (anObject instanceof String) { // 2nd check type
        String anotherString = (String)anObject;
        int n = count;
        if (n == anotherString.count) { // 3rd check lengths
            if (n != 0) { // 4th avoid loading registers from members if length == 0
                int h1 = hash, h2 = anotherString.hash;
                if (h1 != 0 && h2 != 0 && h1 != h2) // 5th check the hashes
                    return false;

Было также обсуждение использования == для интернированных строк (т.е. если обе строки интернированы: if (this != anotherString) return false;).

Ответ 3

1) Вычисление hashCode может быть не быстрее, чем сравнение строк напрямую.

2), если hashCode равен, строки могут все еще не быть равными

Ответ 4

AFAIK. В String можно добавить следующую проверку. Это проверяет, что если хэш-коды установлены, и они разные, то строки не могут быть равны.

if (hash != 0 && anotherString.hash != 0 && hash != anotherString.hash)
    return false;
if (hash32 != 0 && anotherString.hash32 != 0 && hash32 != anotherString.hash32)
    return false;

Ответ 5

Это может быть хорошей идеей для многих случаев использования.

Однако, как класс фундамента, который широко используется во всех видах приложений, автор действительно не знает, может ли эта дополнительная проверка экономить или ухудшать производительность в среднем.

Я предполагаю, что большинство String.equals() вызывается в Hashmap после того, как хэш-коды, как известно, равны, поэтому тестирование хеш-кодов снова бессмысленно.

Если мы рассмотрим сравнение 2 случайных строк, даже с небольшим значением char, подобным US ASCII, очень вероятно, что хеши будут разными, а сравнение char -by- char завершится с ошибкой 1-го char, Так что это будет пустой тратой для проверки хешей.

Ответ 6

Как я думаю, hashCode() может быстрее сравнивать сравнение двух строк.

Аргументы?

Аргументы против этого предложения:

  • Дополнительные операции

hashcode() from String должен получить доступ к каждому символу в строке и выполнить вычисления 2 для каждого символа.
Поэтому нам нужна строка с n символами 5*n операций (загрузка, умножение, поиск/загрузка, умножение, сохранение). Два раза, потому что мы сравниваем две строки. (Хорошо, одно хранилище и одна загрузка на самом деле не учитываются в разумной реализации.)
В лучшем случае это делает операцию 10*x для двух строк с длиной m и n и x=min(m,n). Худший случай 10*x с x=m=n. Среднее где-то между, возможно, (m*n)/2.

Ток равен потребностям реализации в наилучших случаях 3. 2 нагрузки, 1 сравните. Хуже всего 3*x операции для двух строк с длиной m и n и x=m=n. Среднее значение находится где-то между, возможно, 3*(m*n)/2.

  • Даже если мы кэшируем хеш, неясно, что мы что-то сохраняем

Мы должны проанализировать шаблоны использования. Это может быть так, что большую часть времени мы будем спрашивать один раз за равные, а не несколько раз. Даже если мы попросим несколько раз, этого недостаточно, чтобы сэкономить время на кеширование.

Не прямо против скорости, но все же хорошие контраргументы:

  • Счетчик интуитивно понятный

Мы не ожидаем, что hashcode равен equals, потому что мы точно знаем, что hash(a)==hash(b) для некоторого a!=b. Все, кто читает это (и знание хэширования), задаются вопросом, что там происходит.

  • приводит к неудачным примерам/неожиданному поведению

Я уже вижу следующий вопрос о SO: "У меня есть строка с миллиардом раз" а ". Почему навсегда стоит сравнивать ее с равным() против" b "?:)

Ответ 7

Строковый хэш-код недоступен бесплатно и автоматически. Чтобы полагаться на хеш-код, он должен быть рассчитан для обеих строк, и только тогда их можно сравнить. Поскольку возможны столкновения, требуется второе сравнение char -by- char, если хэш-коды равны.

В то время как String кажется неизменным для обычного программиста, у него есть частное поле для хранения своего хэш-кода после его вычисления. Однако это поле вычисляется только тогда, когда требуется сначала хэш-код. Как вы можете видеть из исходного кода String здесь:

 private int hash;

 public int hashCode() {
      int h = hash;
      if (h == 0) {
         ...
         hash = h;  
      }
      return h;
 }

Следовательно, не очевидно, что имеет смысл сначала вычислить хэш-код. Для вашего конкретного случая (возможно, одни и те же экземпляры действительно длинных строк сравниваются друг с другом действительно много раз), это все равно может быть: profile.

Ответ 8

Если хэш-код учитывает все содержимое строки, то вычисление хэш-кода строки с n символами принимает n операций. Для длинных строк это много. Сравнение двух строк принимает n операций, если они одинаковы, не дольше, чем вычисление хэша. Но если строки отличаются друг от друга, то разница, скорее всего, будет найдена намного раньше.

Строковые хэш-функции обычно не учитывают всех символов для очень длинных строк. В этом случае, если я сравниваю две строки, я мог бы сначала сравнить символы, используемые хэш-функцией, и я, по крайней мере, так же быстро, как проверка хэшей. Но если в этих символах нет разницы, то значение хэша будет одинаковым, поэтому мне все равно придется сравнивать все строки.

Сводка. Хорошее сравнение строк никогда не происходит медленнее, но часто намного быстрее, чем сравнение хэшей (и сравнение строк при совпадении хэшей).