Почему я получаю ошибку OutOfMemoryError, изменяющую размер моей реализации HashTable?

1

Я пытаюсь rehash() мой HashTable каждый раз, когда я получаю столкновение, но я продолжаю получать ошибку кучи Java.

В принципе, у меня есть таблица String [], длина которой я хочу умножить на 2 при каждом столкновении в моем хеше.

Изменение: я использую insert() в цикле while, который загружает около 300 000 слов в хеш-таблицу.

 public void rehash() {
        String[] backup = table;
        size = size * 2;
        // i get the error on the line below
        table = new String[size];
        System.out.println("size" + size);
        for (int i = 0; i < backup.length; i++) {
            if (backup[i] != null) {
                insert(backup[i]);
            }

        }

   public void insert(String str) {

        int index = hashFunction(str);

        if (index > size || table[index] != null) {
            rehash();
        }

        table[index] = str;
    }

Моя хэш-функция:

int val= 0;
        val= s.hashCode();
        if (val< 0) {
            val*= -1;
        }

        while (val> this.size) {
            val%= this.size;
        }

        return val;


 public void load() {
        String str = null;
        try {
            BufferedReader in = new BufferedReader(new FileReader(location));
            while ((str = in.readLine()) != null) {
                insert(str);
            }
            in.close();
        } catch (Exception e) {
            System.out.println("exception");
        }
    }
Теги:
hash
hashtable
out-of-memory

3 ответа

0

Независимо от того, насколько большой вы делаете таблицу, вы не можете полностью избежать столкновений. Попробуйте эту программу, например:

System.out.println("Aaa".hashCode());
System.out.println("AbB".hashCode());
System.out.println("BBa".hashCode());
System.out.println("BCB".hashCode());

Выход:

65569
65569
65569
65569

Это четыре разные строки с точно таким же хэш-кодом. Точных столкновений такого рода не так уж и редко. (Хэш-алгоритм, используемый классом Java String, на самом деле не очень хороший, но он поддерживается для обратной совместимости.)

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

Хэш-таблица должна быть подготовлена для решения ограниченного числа столкновений, имея возможность хранить набор разных значений в одном слоте таблицы. Обычно это делается с помощью связанного списка для значений, которые используют один и тот же хэш-код. Текущая реализация java.util.HashMap делает что-то более продвинутое: если значения с одним и тем же хэш-кодом реализуют интерфейс Comparable (как это делает String), он использует это для организации их в двоичном дереве. Существует также что-то возможное, называемое динамическим совершенным хешированием, где столкновениям препятствует динамическое изменение алгоритма хеширования, чтобы гарантировать, что каждое отдельное значение получает четкий хеш, но это сложнее.

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

  • Нет необходимости инициализировать val с помощью 0, если вы сразу же присвоите ему что-то еще на следующей строке. Вместо этого вы можете сделать int val; val = s.hashCode(); int val; val = s.hashCode(); или просто int val = s.hashCode(); ,

  • Проверка: if (val < 0) val *= -1; не является полностью надежным, потому что если val точно равно Integer.MIN_VALUE, умножая его на -1 переполнение и в результате производит Integer.MIN_VALUE. Чтобы полностью исключить отрицательные значения, замаскируйте бит знака целого числа, выполнив val &= Integer.MAX_VALUE; ,

  • Условие здесь неверно: while (val > this.size) val %= this.size; , Он должен быть val >= this.size. Тем не менее, нет необходимости цитировать вообще. Выполнение операции по модулю один раз безоговорочно без каких-либо пока/если этого достаточно. Альтернативно, если вы поддерживаете размер таблицы как точную мощность 2, вы можете реализовать операцию мод как: val &= (size - 1); , что немного быстрее и будет также выполнять требование обеспечения того, чтобы результат был неотрицательным, в отличие от %.

  • В методе insert это должно быть, if (index >= size..., а не if (index > size..., но на самом деле нет необходимости в этой проверке вообще, если хэш-функция уже обеспечивает хэш в диапазоне.

  • Когда слот таблицы уже занят, вам нужно проверить, содержит ли он уже ту же строку, которую вы пытаетесь вставить (в этом случае вы можете немедленно вернуться из метода), а не просто принимать другое значение при столкновении.

0

Из хеш-функции, которую вы опубликовали, не ясно, что она возвращает, но похоже, что у нее есть проблема.

int index = hashFunction(str);

здесь, если ваш индекс не является правильным, чем ваш код делает много рекурсивных новых String [size]. Запустите здесь счетчик или отладочную точку и проверьте.

 if (index > size || table[index] != null) {
                rehash();
            }
  • 0
    я создал счетчик как раз перед rehash (); и результат 7. Вам нужна другая информация? я не понял, чего точно не хватает. я разместил мою хэш-функцию в сообщениях, которые используют string.hashCode (); с некоторым редактированием сразу после.
  • 0
    Ваша хеш-функция возвращает значение, которое здесь не публикуется. Каков был начальный размер вашей таблицы? Если для числа вставленных строк его не нужно было перефразировать 7 раз, то в вашей хэш-функции есть ошибка. В противном случае используйте -Xmx, чтобы обеспечить больше памяти.
Показать ещё 6 комментариев
0

Из javadoc

Как правило, коэффициент загрузки по умолчанию (.75) предлагает хороший компромисс между затратами времени и пространства. Более высокие значения уменьшают объем служебных данных, но увеличивают стоимость поиска (отражается в большинстве операций класса HashMap, включая get и put). Ожидаемое количество записей на карте и коэффициент загрузки должны учитываться при настройке начальной емкости, чтобы минимизировать количество операций перефразирования. Если начальная емкость больше максимального количества записей, деленная на коэффициент нагрузки, никаких операций перефразирования никогда не произойдет.

Если вы знаете, что карта будет использоваться для хранения N записей aprox, хорошая начальная емкость будет N/.75 + N/10 - с учетом дисперсии 10%.

  • Его ОК, чтобы получить ошибку OutOfMemory, но ее не нормально программировать, чтобы перефразировать - постарайтесь, чтобы этого избежать.
  • Для перефразирования - вы не должны дожидаться столкновения. Из класса HashMap,

Этот метод (resize) вызывается автоматически, когда количество ключей на этой карте достигает своего порога

где threshold = (int)(capacity * loadFactor);

  • 0
    как я могу добавить все данные без перефразирования?
  • 0
    Выбирая достаточно высокую начальную емкость?
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню