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

Сериализация и десериализация карты с ключом как строка

Я намерен сериализовать и десериализовать хэш-карту, чей ключ является строкой.

От Джоша Блоха Эффективная Java, я понимаю следующее. С .222

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

Мои вопросы: 1) В общем случае, если переопределить равные и хэш-коды ключевого класса карты, решить эту проблему, и карта может быть правильно восстановлена?

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

3) Раньше я обошел эту проблему, сериализуя массив записей (ключ, значение), и при десериализации я бы восстановил карту. Мне интересно, есть ли лучший подход.

4) Если ответы на вопросы 1 и 2 о том, что они все еще не могут быть гарантированы, может кто-нибудь объяснить, почему? Если хэш-коды одинаковы, они переходят в одни и те же ведра в JVM?

Спасибо, Грейс

4b9b3361

Ответ 1

Форма сериализации java.util.HashMap не сериализует сами ведра, а хэш-код не является частью сохраняемого состояния. Из javadocs:

Последовательные данные:     Емкость HashMap (длина массива ковша) испускается (int), за которым следует размер HashMap (количество ключевых значений сопоставления), за которым следует ключ (Object) и значение (Object) для каждого отображение ключевого значения, представленное HashMap Отображения значений ключа испускаются в том порядке, в котором они возвращается entrySet().iterator().

из http://java.sun.com/j2se/1.5.0/docs/api/serialized-form.html#java.util.HashMap

Постоянное состояние в основном состоит из ключей и ценностей и некоторого домашнего хозяйства. При десериализации хэш-карта полностью перестраивается; ключи перефразируются и помещаются в соответствующие ковши.

Итак, добавление строковых ключей должно работать нормально. Я бы предположил, что ваша ошибка лежит где-то в другом месте.

EDIT: Здесь находится тестовый пример junit 4, который сериализует и десериализует карту, а мини-виртуальные машины изменяют хэш-коды. Тест проходит, несмотря на то, что хэш-коды отличаются от десериализации.

import org.junit.Assert;
import org.junit.Test;

import java.io.*;
import java.util.HashMap;

public class HashMapTest
{
    @Test
    public void testHashMapSerialization() throws IOException, ClassNotFoundException
    {
        HashMap map = new HashMap();
        map.put(new Key("abc"), 1);
        map.put(new Key("def"), 2);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(out);
        objOut.writeObject(map);
        objOut.close();
        Key.xor = 0x7555AAAA; // make the hashcodes different
        ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
        HashMap actual = (HashMap) objIn.readObject();
        // now try to get a value
        Assert.assertEquals(2, actual.get(new Key("def")));
    }

    static class Key implements Serializable
    {
        private String  keyString;
        static int xor = 0;

        Key(String keyString)
        {
            this.keyString = keyString;
        }

        @Override
        public int hashCode()
        {
            return keyString.hashCode()^xor;
        }

        @Override
        public boolean equals(Object obj)
        {
            Key otherKey = (Key) obj;
            return keyString.equals(otherKey.keyString);
        }
    }

}

Ответ 2

Я на 99% уверен, что реализация JVM HashMap и HashSet справляется с этой проблемой. У них есть собственный обработчик сериализации и десериализации. У меня сейчас нет книги Блоха, но я считаю, что он объясняет этот вызов, не говоря о том, что вы не можете надежно сериализовать java.util.HashMap на практике.

Ответ 3

Сериализация хэш-карты:

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

public static void main(String arr[])
{
    Map<String,String> hashmap=new HashMap<String,String>();
    hashmap.put("key1","value1");
    hashmap.put("key2","value2");
    hashmap.put("key3","value3");
    hashmap.put("key4","value4");

    FileOutputStream fos;
    try {
        fos = new FileOutputStream("c://list.ser");

        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(hashmap);
        oos.close();

        FileInputStream fis = new FileInputStream("c://list.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Map<String,String> anotherList = (Map<String,String>) ois.readObject();

        ois.close();

        System.out.println(anotherList);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

Ответ 4

При использовании правильно реализованной хеш-таблицы (например, java.util.HashMap) вам не нужно беспокоиться о методе hashCode() ваших ключей. Метод, упомянутый в пункте № 3 исходного сообщения, фактически встроен в реализацию хорошей хеш-таблицы.

Механизм сериализации по умолчанию переопределен. Вместо этого сохраняется простой список записей (key – value). При десериализации хеш-таблицы метод таблицы put() используется для повторного добавления каждой записи отдельно. Это поддерживает согласованность нового, десериализованного экземпляра хэш-таблицы. Не имеет значения, изменились ли хэш-коды ключей; ведро выбирается на основе хеш-кода ключа во время десериализации.

Ответ 5

Если все остальное не удается, вы можете сериализовать свою карту с помощью JSON или YAML или XML или что-то в этом роде?

Ответ 6

Если вы перечитаете абзац, вы заметите " Поэтому принятие стандартизованной сериализованной формы для хеш-таблицы будет представлять собой серьезную ошибку". Это не значит, что реализации Hash в Java используют значение по умолчанию Я полагаю, что Java использует пользовательскую сериализацию для реализации Hash.

Надеемся, что эта информация полезна.

Ответ 7

Добавьте эти методы в класс, содержащий карту. Вы также должны добавить сериализацию/де-сериализацию любых других полей:

private void writeObject(ObjectOutputStream stream) throws IOException {
    stream.writeInt(map.size());
    for (Entry<String, String> entry : map.entrySet()) {
        stream.writeObject(entry.getKey());
        stream.writeObject(entry.getValue());
    }
}


private void readObject(ObjectInputStream stream) throws IOException,
        ClassNotFoundException {
    int mapSize = stream.readInt();
    for (int i = 0; i < mapSize; i++) {
        String key = (String) stream.readObject();
        String value = (String) stream.readObject();
        map.put(key, value);
    }
}