我遇到了一些做这样的事情的代码:
Map<String,String> fullNameById = buildMap1(dataSource1);
Map<String,String> nameById = buildMap2(dataSource2);
Map<String,String> nameByFullName = new HashMap<String,String>();
Map<String,String> idByName = new HashMap<String,String>();
Set<String> ids = fullNameById.keySet();
for (String nextId : ids) {
String name = nameById.get(nextId);
String fullName = fullNameById.get(nextId);
nameByFullName.put(fullName, name);
idByName.put(name, nextId);
}
我不得不盯着它看几分钟才弄清楚发生了什么。所有这些都相当于id的连接操作和原始地图之一的反转。由于Id,FullName和Name总是1:1:1,在我看来应该有一些方法来简化这一点。我还发现前两个地图从未再次使用,我发现上面的代码有点难以阅读。所以我正在考虑用这样的东西替换它(对我来说)读得更清洁
Table<String, String, String> relations = HashBasedTable.create();
addRelationships1(dataSource1, relations);
addRelationships2(dataSource2, relations);
Map<String,String> idByName = relations.column("hasId");
Map<String,String> nameByFullName = relations.column("hasName");
relations = null; // not used hereafter
在addRelationships1中我做
relations.put(id, "hasFullName", fullname);
在addRelationships2中我的查询产生id
和name
的值我
relations.put(relations.remove(id,"hasFullName"), "hasName", name);
relations.put(name, "hasId", id);
所以我的问题是这些:
relations = null
之后Table对象不会是GC的,我只想传达它在后面的相当冗长的代码部分中没有再次使用。Table
并不为人所知,我在这方面有点担心。另一方面,顶级现在非常明确地说,“从两个来源收集数据并从中制作这两个地图。”我也喜欢这样的事实,它不会让你想知道是否/在哪里使用(或不使用)其他两个地图。请不要在这里进行优化早/晚讨论。我很清楚这个陷阱。如果它在不损害性能的情况下提高可读性,我很满意。性能提升将是一个很好的奖励。
注意:我的变量和方法名称已经过清理,以防止业务领域分散讨论,我绝对不会将它们命名为addRelationships1或datasource1!同样,最终代码当然会使用常量而不是原始字符串。
答案 0 :(得分:17)
所以我自己做了一些迷你基准测试,并得出结论:两种方法在执行时间方面差别不大。我通过数据集大小的交易运行保持正在处理的数据的总大小。我进行了4次运行,并从所有4次运行中选择了每次实施的最低时间。令人放心的是,两种实现在同一次运行中总是最快的。我的代码可以找到here。以下是我的结果:
Case Maps (ms) Table (ms) Table vs Maps
100000 runs of size 10 2931 3035 104%
10000 runs of size 100 2989 3033 101%
1000 runs of size 1000 3129 3160 101%
100 runs of size 10000 4126 4429 107%
10 runs of size 100000 5081 5866 115%
1 run of size 1000000 5489 5160 94%
因此,对于小型数据集,使用Table似乎稍微慢一点。有趣的事情发生在大约100,000,然后100万,表实际上更快。我的数据将在100到1000范围内挂起,因此至少在执行时间内,性能几乎相同。
至于可读性,我的观点是,如果有人试图找出附近发生的事情并阅读代码,那么查看意图将更加容易。如果他们必须实际调试这段代码,可能会有点困难,因为Table
不太常见,需要一些复杂的理解。
我不确定的另一件事是,创建哈希映射是否更有效,或者在随后迭代地图的所有键的情况下直接查询表。然而,这是一个不同的问题:)
而喜剧的结局是,事实上当我进一步分析代码(数百行)时,我发现在记录之外(有问题的值)的nameByFullname.get()的唯一重要用途是传递结果到idByName.get()。所以最后我实际上将构建一个idByFullName映射和一个idByName映射,而不需要任何连接,并且无论如何都要丢弃整个表。但是我想这是一个有趣的SO问题。
答案 1 :(得分:5)
我在这样的代码中迷失的地方就是使用字符串来处理所有内容 - 将错误的字符串作为参数传递太简单了。因此,我建议将它们聚合到一个对象中,并提供用于通过它们的任何部分访问对象的映射。像这应该做的微不足道的事情:
class IdNameAndFullName {
String id, name, fullName;
}
class IdNameAndFullNameMaps {
Map<String, IdNameAndFullName> byId;
Map<String, IdNameAndFullName> byName;
Map<String, IdNameAndFullName> byFullName;
}
您显然可以用IdNameAndFullNameMaps
替换班级Table
。然而,除了使用一个很好的预先存在的数据结构,我发现其中没有任何优点。缺点是:
Table
should be avoided发送Tuple
{/ 1}}