【问题标题】:Finding partial properties查找部分属性
【发布时间】:2016-05-17 08:36:58
【问题描述】:

我有一张地图。 Key 包含 6 个字符串,Properties 类大致如下所示:

public class Properties {
    private String propertyOne;
    private String propertyTwo;
    private String propertyThree;
    private String propertyFour;
    ...
    ...
}

现在假设我在地图中有一些条目如下:

41111 -> {1,2,3,4,5}

41112 -> {1,2,3,4,6}

41234 -> {1,2,345,87,65}

51123 -> {100,200,30000,345,123}

51122 -> {100,200,30000,556,989}

现在,如果我执行map.get("12567"),我会得到所需的属性对象。

我面临的挑战是,我必须创建一个可以保存部分数据的数据结构。通过部分数据,我的意思是如果我做map.get("4111") 我应该得到{1,2,3,4,5} (41111 的属性){1,2,3,4,6} (41112 的属性)的交集 ){1,2,3,4,null}.

同样map.get("41") 应该产生{1,2,null,null,null}

我现在有一个解决方案,我创建了多个 HashMap,其中包含所有可能的部分键及其对应的值,例如:

Map<String, Property>`` keyValuesForOneChar 包含所有可能的单个字符作为键及其对应的值。

Map<String, Property> keyValuesForTwoChars 包含所有可能的两个字符作为键及其对应的值。

我不喜欢这个解决方案,因为它非常简单,而且我认为维护多个哈希图不是一个好主意。另一个问题是我的原始数据数量约为 200000,并且所有排列组合都会创建大量的部分数据,并且由于数量庞大,我认为哈希图的性能会降低。请为这个问题提出一个更好的解决方案。我有以下限制:

  1. 严格来说,解决方案只能是内存中的。
  2. 查找应该更快。这就是为什么如果处理原始数据和准备数据结构需要额外的时间和内存,这应该不是问题。

【问题讨论】:

  • 对于@T。 Clarverie 回答如果需要找到“11”作为键,然后应该与 41111、41112、51123 和 51122 相交,或者搜索总是从键的开头开始,这会很有趣?
  • 它应该总是从头开始。

标签: java data-structures hashmap in-memory


【解决方案1】:

HashMap 绝对不是最适合您的问题的数据结构。由于您的键是字符串,因此您可以实现 trie(也称为前缀树)。

它通过将字符串键拆分为更小的字符串或字符来工作。通过这种方式,您可以存储键的值,也可以存储常见的前缀。也就是说,您可以将“41111”和“41112”的交集存储在公共前缀“4111”上。查找 4111 时,需要 O(m) 步,其中 m 是键的长度,您将能够检索 {1,2,3,4,5} 和 {1,2,3 的交集,4,6} 如果您在树中插入项目时更新交叉点。

检索部分属性

您可以在构建 trie 时更新部分属性。假设您插入一对 (41111, {1,2,3,4,5})。尝试是特定的树,它可能看起来像这样。符号k,v 表示这是一个具有键k 和值v 的节点。

4,{1,2,3,4,5}
      |
1,{1,2,3,4,5}
      |
1,{1,2,3,4,5}
      |
1,{1,2,3,4,5}
      |
1,{1,2,3,4,5}

在路径上的每个节点上,您存储一个部分属性。现在,当插入一对 (41112,{1,2,3,4,6}) 时,更新 trie:

       4,{1,2,3,4,null}
             |
       1,{1,2,3,4,null}
             |
       1,{1,2,3,4,null}
             |
       1,{1,2,3,4,null}
      /                \
1,{1,2,3,4,5}     2,{1,2,3,4,6}

同样,如果您插入 41234,{1,2,345,87,65},它将如下所示:

              4,{1,2,null,null,null}
                         |
              1,{1,2,null,null,null}
             /                      \
       1,{1,2,3,4,null}          2,{1,2,345,87,65}
             |                           |
       1,{1,2,3,4,null}          3,{1,2,345,87,65}
      /                \                 | 
1,{1,2,3,4,5}     2,{1,2,3,4,6}  4,{1,2,345,87,65}

这样做,您只为已插入的项目的公共前缀存储部分属性,您不需要创建所有组合。另外,检索部分属性是使用与检索值相同的算法完成的。

【讨论】:

  • 完全同意..现在我不必完成我的答案^^。 Hashmap(任何地图实现)是完全错误的数据结构。
  • 感谢您的建议。但问题仍然在于获取部分属性。我仍然手动浏览每条记录并找出它。所以,方法是这样的:首先手动查找所有可能的部分数据,然后准备 Trie。
  • 我添加了一个我想到的示例,这比保留 6 个不同的 HashMap 并计算所有可能的组合和部分属性更有效。
  • 最后一个疑问。正如我所见,Trie 的检索操作是否会比 HashMap 花费更多的时间来获取我必须从一个节点遍历到另一个节点的任何键的属性,而对于 HashMap 它将只是一个基于索引的检索(假设我的哈希函数是高效)。
  • 这是一个有趣的问题。根据我的经验,我针对 java 的 HashMap 对基本 trie(未优化)进行了基准测试,我的 trie 在 put/get/delete 的随机序列上的效率比 hashmap 低几个百分点。 (虽然键更长〜15-20个字符)。所以,我的回答是:我不知道,你需要对它进行基准测试才能得到答案。但是通过正确的实现,我相信你不会有疯狂的性能损失。通过一些优化,您很可能可以与 HashMap 竞争。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-30
  • 1970-01-01
  • 1970-01-01
  • 2019-07-12
  • 1970-01-01
  • 1970-01-01
  • 2018-03-17
相关资源
最近更新 更多