PDFBox SmallMap не соблюдает контракт хэш-кода Map.Entry

Я работаю над этапом оптимизации для объединения ресурсов в PDF-документе с помощью PDFBox.

После многих часов исследования того, почему мой код генерирует слишком много хеш-коллизий, я с удивлением обнаружил потенциальную ошибку в последней версии PDFBox.

Позвольте мне объяснить: в COSDictionary набор записей поддерживается SmallMap. Если вы хотите вычислить хэш-код Map.Entry, сгенерированный объектом входа COSDictionary, вы обнаружите, что хэш-код будет рассчитываться следующим образом.

Мой код:

if (object instanceof COSDictionary) {
                int result = 3;
                for (Map.Entry<COSName, COSBase> entry : ((COSDictionary) object).entrySet())
                    result += entry.hashCode();
                if (object instanceof COSStream) { .... ..... ....}

В классе SmallMap и внутреннем классе SmallMapEntry (PDFBox):

public int hashCode() {
            return this.getKey().hashCode();
        }

Но в общем контракте Map.Entry прямо указано (JavaDoc):

         /**
         * Returns the hash code value for this map entry.  The hash code
         * of a map entry {@code e} is defined to be: <pre>
         *     (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
         *     (e.getValue()==null ? 0 : e.getValue().hashCode())
         * </pre>
         * This ensures that {@code e1.equals(e2)} implies that
         * {@code e1.hashCode()==e2.hashCode()} for any two Entries
         * {@code e1} and {@code e2}, as required by the general
         * contract of {@code Object.hashCode}.
         *
         * @return the hash code value for this map entry
         * @see Object#hashCode()
         * @see Object#equals(Object)
         * @see #equals(Object)
         */

int hashCode();

Я думаю, что это ошибка в PDFBox, но я не уверен, что вы об этом думаете?

Интерфейс Map.Entry, сюр!

Guyard 17.05.2024 09:32
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
4
1
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Действительно, метод SmallMapEntryhashCode не реализован так, как определено в JavaDocs Map.Entry. Аналогично, SmallMapEntry.equals не реализуется так, как определено для Map.Entry.equals, SmallMap.hashCode не так, как определено для Map.hashCode, и SmallMap.equals не так, как определено для Map.equals.

Но для этого есть причина, и это не просто лень или производительность: сами Map JavaDocs объясняют недостаток определений hashCode и equals:

/**
 * ...
 * <p>Some map operations which perform recursive traversal of the map may fail
 * with an exception for self-referential instances where the map directly or
 * indirectly contains itself. This includes the {@code clone()},
 * {@code equals()}, {@code hashCode()} and {@code toString()} methods.
 * ...

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

Очень понятное объяснение, спасибо. Это также объясняет, почему у меня возникли большие проблемы со Stackoverflow теперь, когда я вычислил более точный хеш-код... Фактически, даже отладчик аварийно завершает работу из-за рекурсии toString, вызванной бесконечным циклом в COSDictionnary, когда отладчик пытается для отображения имени объекта. Нелегко работать. Спасибо, подумаю, как улучшить.

Guyard 17.05.2024 09:37

Другие вопросы по теме

Похожие вопросы