Для многих алгоритмов комплекса один из сохраняемых параметров — выборка последовательностей скрытых состояний (то есть геном или набор белков); в частности, выборки используются для всех алгоритмов распознавания. Объем информации, соответствующий такой выборке, составляет несколько десятков или даже сотен мегабайтов, так что ее хранение при сериализации алгоритма вызывает определенные трудности. Более того, одна и та же выборка (соответствующая, например, геному какого-то биологического вида) может быть общей для многих алгоритмов.
Для того чтобы не сохранять каждый раз все данные из выборки, можно определить текстовые идентификаторы для базовых выборок, соответствующих файлам на жестком диске. Соответствие между идентификаторами и файлами определяется окружением с помощью конфигурационного файла. Использование идентификаторов позволяет решить сразу несколько проблем:
- файлы, в которых хранятся выборки, можно переносить без потери работоспособности сохраненных данных;
- одному файлу может соответствовать несколько идентификаторов (например, в целях обратной совместимости).
Логика сериализации выборки не является универсальной: в некоторых случаях (например, если необходимо сохранять результаты работы алгоритма распознавания) имеет смысл все-таки записывать все содержимое выборки. Поэтому в комплексе базовые операции над выборками (выбор строк, объединение, фильтрация и т. п.) реализованы в виде класса SimpleSequenceSet, а функциональность, которая соответствует необычному механизму сериализации, сосредоточена в дочернем классе NamedSequenceSet.
Итак, в простейшем случае выборка сохраняется в виде строки-идентификатора. При десериализации с помощью окружения этому идентификатору ставится в соответствие файл, из которого загружаются данные выборки. Чтобы окружение в этот момент было доступно, десериализация производится при помощи специального класса, дополняющего функциональность стандартного потока чтения ObjectInputStream).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class Env implements Representable { /** * Поток для загрузки данных, предоставляющий доступ к окружению, * в пределах которого выполняется загрузка. */ public static class ObjInputStream extends ObjectInputStream { public final Env env; public ObjInputStream(InputStream in, Env env) throws IOException { super(in); this.env = env; } } /** * Заргужает именованную выборку. * * @param datasetName * имя выборки, которую нужно загрузить */ public SequenceSet loadSet(String datasetName) throws IOException { // код пропущен } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class NamedSequenceSet extends SimpleSequenceSet { /** * Название выборки (выборок), которое использовалось * для создания контейнера. */ private String datasetName = null; // код пропущен private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Env env = ((Env.ObjInputStream) in).env; if (this.datasetName != null) { try { // загружает выборку с указанным идентификатором addSet(env.loadSet(datasetName)); } catch (IOException e) { } } } } |
На практике одной строки для хранения выборки недостаточно: необходимо, чтобы могли запоминаться выборки, над которыми произошли некоторые манипуляции. Скажем, для задачи распознавания фрагментов генов имеет смысл отдельно оценить качество распознавания для «коротких» генов и «длинных» генов. Можно выделить две базовые операции над выборками:
- объединение нескольких выборок;
- выбор подмножества строк из выборки.
Этим операциям соответствуют три сериализуемых поля:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class NamedSequenceSet extends SimpleSequenceSet { /** * Базовая выборка, при фильтрации которой была получена * эта выборка. */ private NamedSequenceSet unfilteredSet = null; /** * Селектор, которой использовался при создании выборки * путем фильтрации базовой выборки. */ private boolean[] selector = null; /** * Части, из которых составлена эта выборка. */ private SequenceSet[] setParts = null; } |
Операции объединения и фильтрации присваивают значения соответствующим полям.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class NamedSequenceSet extends SimpleSequenceSet { public SequenceSet join(SequenceSet other, SequenceSet... more) { // создает новую выборку, объединяющую эту выборку и другие NamedSequenceSet union = new NamedSequenceSet( super.join(other, more)); union.setParts = new SequenceSet[2 + more.length]; union.setParts[0] = this; union.setParts[1] = other; for (int i = 0; i < more.length; i++) { union.setParts[2 + i] = more[i]; } return union; } public SequenceSet filter(boolean[] selector) { // создает подмножество этой выборки NamedSequenceSet filtered = new NamedSequenceSet( super.filter(selector)); filtered.unfilteredSet = this; filtered.selector = selector.clone(); return filtered; } } |
При этом полагается, что с точки зрения пользователя выборки являются неизменяемыми; иначе бы при присвоении полей unfilteredSet
и элементов массива setParts
понадобилась бы операция клонирования выборок.
Результат выполнения методов join
и filter
можно представить в виде ациклического ориентированного графа (вершины — выборки, ребра — операции). Корректность сохранения и восстановления графа обеспечивается встроенными механизмами Java — объектными потоками чтения / записи (ObjectInputStream, ObjectOutputStream).

При десериализации выборки достаточно повторить операцию, которая привела к ее созданию.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class NamedSequenceSet extends SimpleSequenceSet { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Env env = ((Env.ObjInputStream) in).env; if (datasetName != null) { // код пропущен } else if (unfilteredSet != null) { SequenceSet filtered = unfilteredSet.filter(selector); this.addSet(filtered); // this и filtered совпадают по содержимому } else if (setParts != null) { for (SequenceSet part : this.setParts) { this.addSet(part); } // в this содержатся прецеденты из всех выборок // в массиве setParts } } } |