Я намереваюсь сериализировать и десериализовать hashmap, ключ которого является строкой.
От Эффективного Java Josh Bloch я понимаю следующее. P.222
Например, рассмотрите случай хеш-таблицы. Физическое представление является последовательностью блоков хеша, содержащих записи значения ключа. То, которые объединяют запись в блок, помещается в, функция хэш-кода ключа, который не является, в целом гарантирован быть тем же от реализации JVM до реализации JVM. На самом деле это, как даже гарантируют, не будет тем же от выполненного для работы той же реализации JVM. Поэтому принятие сериализированной формы значения по умолчанию для хеш-таблицы составит серьезную ошибку. Сериализация и десериализация хеш-таблицы могли привести к объекту, инварианты которого были серьезно повреждены.
Мои вопросы: 1) В целом, был бы, переопределяя равняться, и хэш-код ключевого класса карты решают этот вопрос, и карта может быть правильно восстановлена?
2) Если моим ключом является Строка, и Строковый класс уже переопределяет хэш-код () метод, был бы я все еще описывать проблему выше. (Я вижу ошибку, которая заставляет меня думать, что это - вероятно, все еще проблема даже при том, что ключ является Строкой с переопределением хэш-кода.)
3) Ранее, я обошел эту проблему путем сериализации массива записей (ключ, значение), и при десериализации я восстановлю карту. Я задаюсь вопросом, существует ли лучший подход.
4) Если ответы на вопрос 1 и 2 - то, что он все еще не может быть гарантирован, кто-то мог объяснить почему? Если бы хэш-коды являются тем же, они перешли бы к тем же блокам через JVMs?
Спасибо, изящество
Форма сериализации java.util.HashMap
не сериализует сами сегменты, и хэш-код не является частью постоянного состояния. Из javadocs:
Последовательные данные: Емкость HashMap (длина массива сегментов) выдается (int), за которым следует размер HashMap (количество сопоставлений "ключ-значение" ), за которым следуют ключ (объект) и значение (объект) для каждого сопоставления "ключ-значение" , представленного {{1 }} HashMap Сопоставления "ключ-значение" генерируются в том порядке, в котором они возвращаются
entrySet (). Iterator ()
.
из http://java.sun.com/j2se/1.5.0/docs/api/serialized-form.html#java.util.HashMap
Постоянное состояние в основном состоит из ключей и значений. и немного домашнего хозяйства. При десериализации хэш-карта полностью перестраивается; ключи перефразируются и помещаются в соответствующие корзины.
Итак, добавление строковых ключей должно работать нормально. Я предполагаю, что ваша ошибка кроется в другом месте.
РЕДАКТИРОВАТЬ: Вот тестовый пример junit 4, который сериализует и десериализует карту, а виртуальные машины minics меняют хэш-коды. Тест проходит, несмотря на то, что после десериализации хэш-коды отличаются.
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);
}
}
}
Если ничего не помогает, можете ли вы сериализовать свою карту с помощью JSON, YAML, XML или чего-то еще?
Я на 99% уверен, что реализация JVM HashMap и HashSet справится с этой проблемой. У них есть собственный обработчик сериализации и десериализации. У меня сейчас нет книги Блоха, но я считаю, что он объясняет проблему, не говоря, что вы не можете надежно сериализовать java.util.HashMap на практике.
При использовании правильно реализованной хеш-таблицы (например, java.util.HashMap
) вам не нужно беспокоиться о hashCode ()
ваших ключей. Техника, упомянутая в пункте 3 исходного сообщения, фактически встроена в хорошую реализацию хеш-таблицы.
Механизм сериализации по умолчанию отменен. Вместо этого сохраняется простой список пар записей (ключ – значение). При десериализации хэш-таблицы метод таблицы put ()
используется для повторного добавления каждой записи по отдельности. Это поддерживает согласованность нового десериализованного экземпляра хэш-таблицы. Не имеет значения, изменились ли хэш-коды ключей; ведро выбирается на основе хэш-кода ключа во время десериализации.