Я должен написать версию для большого количества операций нашего веб-приложения.
позволяет делать на более ограниченной основе из пользовательского интерфейса. Желаемый
Операция заключается в назначении объектов категории. Категория может иметь
несколько объектов, но данный объект может быть только в одной категории.
Рабочий процесс для задачи:
1) Используя браузер, загружается файл следующей формы:
# ObjectID, CategoryID
Oid1, Cid1
Oid2, Cid1
Oid3, Cid2
Oid4, Cid2
[etc.]
Файл, скорее всего, будет содержать от десятков до сотен строк, но
определенно может иметь тысячи строк.
В идеальном мире данный идентификатор объекта встречается в файле только один раз.
(отражает тот факт, что объект может быть отнесен только к одной категории)
Но так как файл создан вне нашего контроля, нет никакой гарантии
это действительно так, и обработка должна учитывать эту возможность.
2) Сервер получит файл, проанализирует его, предварительно обработает его
и показать страницу что-то вроде:
723 objects to be assigned to 126 categories
142 objects not found
42 categories not found
Do you want to continue?
[Yes] [No]
3) Если пользователь нажмет кнопку Yes
, сервер
на самом деле делать работу.
Поскольку я не хочу анализировать файл на обоих этапах (2) и (3), как
часть (2), мне нужно построить контейнер, который будет жить через
запросы и держать полезное представление данных, которые позволят мне
легко предоставить данные для заполнения страницы «предварительного просмотра» и позволит мне
эффективно выполнять реальную работу. (Хотя, очевидно, у нас есть сессии, мы
обычно сохраняет очень мало состояния сеанса в памяти.)
Существует существующий
assignObjectsToCategory(Set<ObjectId> objectIds, CategoryId categoryId)
функция, которая используется, когда назначение выполняется через пользовательский интерфейс. это
очень желательно, чтобы массовая операция также использовала этот API, так как
делает кучу другой бизнес-логики в дополнение к простой
назначение и нам нужна та же бизнес-логика для запуска, когда эта масса
присвоение сделано.
Изначально все было в порядке, если файл "незаконно" указан
несколько категорий для данного объекта - было бы хорошо назначить
объект abitrarily к одной из категорий файла, связанного с ним
с.
Итак, я изначально думал, что на шаге (2), когда я проходил через
файл, который я бы собрал и поместил в контейнер перекрестных запросов
Map<CategoryId, Set<ObjectId>>
(в частности, HashMap
для быстрого
поиск и вставка), а затем, когда пришло время сделать работу, я мог
просто итерации на карте и для каждого CategoryId
вытащите
Set<ObjectId>
и передать их в assignObjectsToCategory()
.
Однако изменилось требование к обработке дубликатов ObjectId
.
И теперь они должны быть обработаны следующим образом:
- Если
ObjectId
появляется несколько раз в файле и
все время связано с тем же CategoryId
, назначьте
объект этой категории.
- Если в файле несколько раз появляется
ObjectId
и
связано с различными CategoryId
с, считайте, что
ошибку и упомяните об этом на странице предварительного просмотра.
Кажется, это испортило мою стратегию Map<CategoryId, Set<ObjectId>>
так как он не обеспечивает хороший способ обнаружить, что ObjectId
I
только что прочитанное из файла уже связано с CategoryId
.
Поэтому мой вопрос заключается в том, как наиболее эффективно обнаруживать и отслеживать эти
дубликат ObjectId
с?
То, что пришло в голову, это использовать как «прямую», так и «обратную» карты:
public CrossRequestContainer
{
...
Map<CategoryId, Set<ObjectId>> objectsByCategory; // HashMap
Map<ObjectId, List<CategoryId>> categoriesByObject; // HashMap
Set<ObjectId> illegalDuplicates;
...
}
Затем, после считывания каждой пары (ObjectId, CategoryId)
, будет
попасть в обе карты. Как только файл был полностью прочитан, я
мог сделать:
for (Map.Entry<ObjectId, List<CategoryId>> entry : categoriesByObject.entrySet()) {
List<CategoryId> categories = entry.getValue();
if (categories.size() > 1) {
ObjectId object = entry.getKey();
if (!all_categories_are_equal(categories)) {
illegalDuplicates.add(object);
// Since this is an "illegal" duplicate I need to remove it
// from every category that it appeared with in the file.
for (CategoryId category : categories) {
objectsByCategory.get(category).remove(object);
}
}
}
}
Когда этот цикл завершится, objectsByCategory
больше не будет содержать «незаконных»
дубликаты, и illegalDuplicates
будет содержать все «незаконные» дубликаты для
доложить по мере необходимости. Затем я могу перебрать objectsByCategory
, получить Set<ObjectId>
для каждой категории и позвонить assignObjectsToCategory()
, чтобы выполнить назначения.
Но хотя я думаю, что это сработает, я беспокоюсь о сохранении данных дважды, особенно
когда входной файл огромен. И я также обеспокоен тем, что мне не хватает чего-то ре: эффективности
и это будет идти очень медленно.
Есть ли способы сделать это, которые не будут использовать двойную память, но все еще могут работать быстро?Я что-то упустил, что даже при двойном использовании памяти все равно будет много
медленнее, чем я ожидаю?