我必须为我们的 web 应用程序编写一个批量操作版本,让您在 UI 的基础上进行更有限的操作。所需的操作是将对象分配给一个类别。一个类别可以有多个对象,但给定的对象只能属于一个类别。
该任务的工作流程是:
1) 使用浏览器,上传如下形式的文件:
# ObjectID, CategoryID
Oid1, Cid1
Oid2, Cid1
Oid3, Cid2
Oid4, Cid2
[etc.]
该文件很可能有数十到数百行,但绝对可能有数千行。
在理想的世界中,给定的对象 id 只会在文件中出现一次(反映了一个对象只能分配给一个类别的事实)但是由于文件是在我们的控制之外创建的,因此不能保证这实际上是真的并且处理必须处理这种可能性。
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)
通过 UI 完成分配时使用的函数。非常希望批量操作也使用此 API,因为除了简单分配之外,它还执行大量其他业务逻辑,并且在完成此批量分配时我们需要运行相同的业务逻辑。
最初,如果文件“非法”为给定对象指定了多个类别,这将是可以的——将对象任意分配给与其关联的文件的类别之一是可以的。
所以我最初认为,在步骤 (2) 中,当我浏览文件时,我将构建并放入交叉请求容器 a
Map<CategoryId, Set<ObjectId>>(特别是HashMap用于快速查找和插入),然后当我需要完成工作时可以只在地图上进行迭代,并为每个CategoryId拉出关联Set<ObjectId>并将它们传递到assignObjectsToCategory().
但是,关于如何处理重复ObjectIds 的要求发生了变化。现在将按如下方式处理它们:
- 如果一个
ObjectId在文件中出现多次并且所有时间都与同一个相关联,则CategoryId将该对象分配给该类别。 - 如果 an
ObjectId在文件中多次出现并与不同CategoryId的 s 相关联,则认为这是一个错误并在“预览”页面上提及它。
这似乎弄乱了我的Map<CategoryId, Set<ObjectId>>策略,因为它没有提供一种很好的方法来检测ObjectId我刚刚从文件中读出的文件是否已经与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()来完成任务。
但是虽然我认为这会起作用,但我担心将数据存储两次,尤其是当输入文件很大时。而且我也担心我错过了一些东西:效率,这会非常缓慢。
有没有办法做到这一点,不会使用双内存但仍然可以快速运行?我是否遗漏了一些即使使用双倍内存仍会比我预期的慢很多的东西?