番石榴表的效率与多个哈希映射

Gus*_*Gus 5 java performance guava

我遇到了一些代码正在做这样的事情:

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);
}
Run Code Online (Sandbox Code Playgroud)

我不得不盯着它看几分钟,弄清楚发生了什么.所有这些都相当于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
Run Code Online (Sandbox Code Playgroud)

在addRelationships1我做

relations.put(id, "hasFullName", fullname);
Run Code Online (Sandbox Code Playgroud)

在addRelationships2中我的查询产生的值idname我一样

relations.put(relations.remove(id,"hasFullName"), "hasName", name);
relations.put(name, "hasId", id);
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:

  1. 通过处理器或内存或GC负载,我所做的工作是否存在潜在的低效率?我不这么认为,但我并不熟悉Table的效率.我知道Table对象之后不会是GC relations = null,我只是想传达它在后面的相当冗长的代码部分中没有再次使用它.
  2. 我获得了效率吗?我一直在令人信服并且不相信我自己有没有.
  3. 你觉得这个更具可读性吗?或者这对我来说很容易阅读,因为我写了它?由于事实Table并不为人所知,我对这方面有点担心.另一方面,顶级现在非常明确地说,"从两个来源收集数据并从中制作这两个地图." 我也喜欢这样的事实,它不会让你想知道是否/在哪里使用(或不使用)其他两个地图.
  4. 你有比上述任何一种更好,更清洁,更快捷,更简单的方法吗?

请不要在这里进行优化早期/晚期讨论.我很清楚这个陷阱.如果它在不损害性能的情况下提高可读性,我很满意.性能提升将是一个很好的奖励.

注意:我的变量和方法名称已在此处进行了清理,以防止业务区域从讨论中分散注意力,我绝对不会将它们命名为addRelationships1或datasource1!同样,最终代码当然会使用常量而不是原始字符串.

Gus*_*Gus 17

所以我自己做了一些迷你基准测试,并得出结论:两种方法在执行时间方面差别不大.我通过数据集大小的交易运行保持正在处理的数据的总大小.我进行了4次运行,并从所有4次运行中选择了每次实施的最低时间.令人放心的是,两种实现在同一次运行中总是最快的.我的代码可以在这里找到.这是我的结果:

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%
Run Code Online (Sandbox Code Playgroud)

因此,对于小型数据集,使用Table似乎稍微慢一些.有趣的事情发生在大约100,000,然后100万,表实际上更快.我的数据将在100到1000范围内挂起,因此至少在执行时间内,性能几乎相同.

至于可读性,我的观点是,如果有人试图找出附近发生的事情并阅读代码,那么查看意图将更加容易.如果他们必须实际调试这段代码,可能会有点困难,因为Table不太常见,需要一些复杂的理解.

我不确定的另一件事是,创建哈希映射是否更有效,或者在随后迭代地图的所有键的情况下直接查询表.然而,这是一个不同的问题:)

而喜剧的结局是,事实上,当我进一步分析代码(数百行)时,我发现在记录之外(有问题的值)的nameByFullname.get()的唯一重要用途是将结果传递给idByName .得到().所以最后我实际上将构建一个idByFullName映射和一个idByName映射,而不需要任何连接,并且无论如何都要丢弃整个表.但是我想这是一个有趣的SO问题.


maa*_*nus 5

tl;博士,但我担心你需要在原设计上迈出更大的一步.模拟数据库表可能是一个很好的练习,但对我来说,你的代码实际上并不可读.

  1. 在我所做的事情中是否存在潜伏的低效率......不知道.
  2. 我获得了效率吗?我担心你需要先测量它.删除一些间接确实有帮助,但使用更复杂的数据结构可能会抵消它.总的来说,性能太复杂了.
  3. 你觉得这个更具可读性吗?恐怕不是.
  4. 你有比上述任何一种更好,更清洁,更快捷,更简单的方法吗?希望如此....

我在这样的代码中迷失的地方就是使用字符串来处理所有内容 - 将错误的字符串作为参数传递太简单了.所以我建议将它们聚合成一个对象,并提供用于通过它们的任何部分访问对象的映射.像这应该做的微不足道的事:

class IdNameAndFullName {
    String id, name, fullName;
}

class IdNameAndFullNameMaps {
    Map<String, IdNameAndFullName> byId;
    Map<String, IdNameAndFullName> byName;
    Map<String, IdNameAndFullName> byFullName;
}
Run Code Online (Sandbox Code Playgroud)

显然你可以IdNameAndFullNameMaps用a 替换这个类Table.然而,除了使用一个很好的预先存在的数据结构,我发现其中没有任何优点.缺点是:

  • 效率低下
  • 失去可读性(我不会Table在这里使用同样的理由Tuple 应该避免)
  • 使用String键(您的"hasId"和"hasName").