По разным причинам у меня есть собственная сериализация, в которой я выгружаю некоторые довольно простые объекты в файл данных. Существует, может быть, 5-10 классов, а полученные графы объектов ацикличны и довольно просты (каждый сериализованный объект имеет 1 или 2 ссылки на другой сериализованный объект). Например:
class Foo
{
final private long id;
public Foo(long id, /* other stuff */) { ... }
}
class Bar
{
final private long id;
final private Foo foo;
public Bar(long id, Foo foo, /* other stuff */) { ... }
}
class Baz
{
final private long id;
final private List<Bar> barList;
public Baz(long id, List<Bar> barList, /* other stuff */) { ... }
}
Поле id предназначено только для сериализации, поэтому, когда я сериализую в файл, я могу записывать объекты, сохраняя запись о том, какие идентификаторы были сериализованы до сих пор, а затем для каждого объекта проверяя, были ли сериализованы его дочерние объекты, и записывая те, которые этого не сделали, наконец, записывают сам объект, записывая его поля данных и идентификаторы, соответствующие его дочерним объектам.
Что меня озадачивает, так это то, как назначить идентификаторы. Я подумал об этом, и вроде бы есть три случая присвоения ID:
- динамически создаваемые объекты -- идентификатор назначается из счетчика, который увеличивается
- чтение объектов с диска -- id присваивается из числа, хранящегося в файле на диске
- одноэлементные объекты — объект создается до любого динамически создаваемого объекта, чтобы представлять одноэлементный объект, который всегда присутствует.
Как я могу справиться с этим правильно? Я чувствую, что заново изобретаю велосипед, и должна быть хорошо налаженная техника для обработки всех случаев.
пояснение: в качестве дополнительной информации, формат файла, на который я смотрю, примерно следующий (замалчиваем некоторые детали, которые не должны иметь значения). Он оптимизирован для обработки довольно большого объема плотных двоичных данных (десятки/сотни МБ) с возможностью вкрапления в него структурированных данных. Плотные двоичные данные составляют 99,9% размера файла.
Файл состоит из серии блоков с исправлением ошибок, которые служат контейнерами. Каждый блок можно рассматривать как содержащий массив байтов, состоящий из серии пакетов. Пакеты можно читать по одному (например, можно определить, где заканчивается каждый пакет, и сразу после этого начинается следующий).
Таким образом, файл можно рассматривать как серию пакетов, хранящихся поверх уровня исправления ошибок. Подавляющее большинство этих пакетов представляют собой непрозрачные двоичные данные, которые не имеют никакого отношения к этому вопросу. Однако небольшая часть этих пакетов представляет собой элементы, содержащие сериализованные структурированные данные, образующие своего рода «архипелаг», состоящий из «островов» данных, которые могут быть связаны отношениями ссылок на объекты.
Таким образом, у меня может быть файл, в котором пакет 2971 содержит сериализованный Foo, а пакет 12083 содержит сериализованный Bar, который ссылается на Foo в пакете 2971 (с пакетами 0-2970 и 2972-12082, являющимися непрозрачными пакетами данных)
Все эти пакеты являются неизменяемыми (и, следовательно, учитывая ограничения конструкции объектов Java, они образуют ациклический граф объектов), поэтому мне не приходится иметь дело с проблемами изменчивости. Они также являются потомками общего интерфейса Item
. Что я хотел бы сделать, так это записать в файл произвольный объект Item
. Если Item
содержит ссылки на другие Item
, которые уже есть в файле, мне также нужно записать их в файл, но только если они еще не были записаны. В противном случае у меня будут дубликаты, которые мне нужно будет как-то объединить, когда я их прочитаю.