【问题标题】:How to get key from the reference of a hash element如何从哈希元素的引用中获取密钥
【发布时间】:2010-08-01 01:28:46
【问题描述】:

假设 $my_ref = \$hash{'mary'}; #my_ref 是哈希元素的引用点。
....
稍后,我如何使用$my_ref 来检索它指向的哈希元素的键?即如何从$my_ref获取字符串'mary'?

我问这个问题是因为我有几组用户名列表,一些用户名出现在多个组中,这会消耗内存。所以我决定创建一个普通的用户名列表,让这些组只存储对应用户名的引用而不是用户名。

例如原来,

%group1 = {'mary'=>1, 'luke'=1,'tom'=1,...}  
%group2 = {'mary'=>1, 'sam'=1,'tom'=1,...}

在这里你看到'mary' 和'tom' 显示在group1group2 中,它们会消耗内存。 (注意我不关心这个例子中的值,值在这里只是因为数据结构是一个哈希)。所以为了减少内存,我希望有一个通用列表存储所有用户名:

%common_hash = {'mary'=>1, 'luke'=1,'tom'=1,'sam'=1...};  
$ref1 = \$common_hash{'mary'};  
$ref2 = \$common_hash{'luke'};  
$ref3 = \$common_hash{'tom'};  
$ref4 = \$common_hash{'sam'};

组只存储哈希元素的引用:

%group1 = {$ref1=>1, $ref2=1,$ref3=1,...};  
%group2 = {$ref1=>1, $ref4=1,$ref3=1,...}; 

我认为这种方法可以节省很多内存,因为:

  1. 一个用户名在内存中存储一​​次而不是多次;
  2. groups 存储引用(整数)而不是字符串(在我的例子中,每个用户名的长度平均为 30 个字节,而每个整数只有 4 个字节(32 位系统)或 8 个字节(64 位系统.)) (顺便说一句,如果整数不使用 4 个字节或 8 个字节,请纠正我。)
  3. 使用参考我可以立即访问用户名而无需查找。

但是如何从组中获取用户名?

如果我使用@my_ref = keys %group1,我想我会得到'mary'的值,而不是'mary'。

$result = $($my_ref[0]);

【问题讨论】:

  • %group1=(key=>value,...)%group1={key=>value,...} 做了非常不同的事情。如果您对此一无所知,请启用warnings

标签: perl reference hashref


【解决方案1】:
  1. 引用不是整数;它是一个 SV,所以它将是 24 个字节,而不是 4 个字节。

  2. 没关系,因为您没有存储引用,因为哈希键始终是字符串%group1 等哈希的键实际上是看起来像“HASH(0x19838e2)”的字符串,这是没用的。

  3. 这并不重要,因为 Perl 足够聪明,可以避免在多个哈希中使用相同的字符串作为键时浪费内存。没错,如果您只是以简单、明显、明智的方式来做事情,那么 perl 将使用 更少的内存,而不是您尝试做的复杂事情。

【讨论】:

    【解决方案2】:

    抱歉,哈希不能这样工作。您不会通过使用引用而不是字符串作为哈希键来节省任何内存,而且您是:

    1. 使得在哈希中查找数据变得更加困难(它被隐藏了)
    2. 妨碍 Perl 的内部散列优化(使用 散列算法 在实际上是一个列表的内容中提供 O(1) 查找)。

    在任何一种情况下,散列键都是一个标量,它需要存储在某个地方。通过使用引用作为哈希键,现在您不仅需要将引用存储在哈希中,还需要存储它所引用的值,因此您现在正在使用更多内存。

    是什么让你相信你通过你的,,新颖的方法来保存记忆?您是否针对不同的实现运行了内存分析器?

    通常,您无法从哈希的 value 中返回它的 key(尽管如果它是唯一的,您可以线性遍历哈希表来查找它)。如果你想同时跟踪哈希键和值,你需要自己做。一些常见的方法是:

    # iterate through the table by key
    foreach my $key (keys %hash)
    {
         # here we have both the key and its corresponding value
         print "value at key $key is $hash{$key}\n";
    }
    
    # iterate through the table by keys and values
    while (my ($key, $value) = each %hash)
    {
         print "value at key $key is $value, which is the same as $hash{$key}\n";
    }
    

    请阅读manual 中哈希的工作原理。您还可以阅读有关 keyseach 函数的信息。

    【讨论】:

    • “没有节省内存”的假设是错误的,因为它错误地假设哈希键小于它的引用,并且没有重复。如果您想像这样存储密钥,它会 BE 因为它们很长并且使用了不止一次......也就是说,其余的答案几乎可以,除了粗鲁和公然缺乏礼貌。
    • @cnd 这也不是真的 - 多次使用的键不会占用更多内存,因为涉及到额外的缓存层。不过,我必须深入研究 XS 文档才能找到确切的参考。
    【解决方案3】:

    散列是一种将名称与标量相关联的方法。如果你有一个散列和一个键,你就有一个标量,而不是对散列桶的引用或类似的东西。

    my $value = $hash{name};
    

    只是一个标量。

    my $ref = \$hash{name};
    

    只是对标量的引用。没有比匿名引用更能包含允许您回溯到散列键的信息的能力,无法告诉您符号表或词法键盘上的名称可能是什么(无需帮助)。

    【讨论】:

      【解决方案4】:

      试着把它想象成数据库表。有一个用户“表”/散列将用户 ID 与用户信息相关联,并让其他散列使用用户 ID,而不是用户信息。

      my $userid = 5;
      $user->{$groupid};
      # would be the hash element for that user with a user id 
      

      然后您可以让您的群组列表使用数字而不是名称/用户名。

      但是,我认为您为自己做的工作超出了需要。你真的遇到过这个程序使用太多内存的问题吗?除非您的键包含非常大的字符串,否则重复键不是问题。

      如果您有一千个不同的用户名(全部为 100 个字符或更少)并且组合在一起,就有 10,000 个用户/组关系,那么您只有:

      100 字节 * 10,000 = 1MB

      老实说,大多数名称都是该大小的 1/5:200 KB

      我的建议是,只有当您拥有大量 MB 信息(例如 500 或更多)时才需要担心这一点。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-24
        • 1970-01-01
        • 1970-01-01
        • 2015-02-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-23
        相关资源
        最近更新 更多