4

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

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

例如最初,

%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. 组存储引用(整数)而不是字符串(在我的情况下,每个用户名的长度平均为 30 个字节,而每个整数只有 4 个字节(32 位系统)或 8 个字节(64 位系统)) (顺便说一句,如果整数不使用 4 个字节或 8 个字节,请纠正我。)
  3. 使用参考我可以立即访问用户名而无需查找。

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

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

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

4 回答 4

5
  1. 引用不是整数;这是一个 SV,所以它将是 24 个字节,而不是 4 个字节。

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

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

于 2010-08-01T02:28:12.303 回答
4

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

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

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

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

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

# 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";
}

请阅读手册中的哈希是如何工作的。您还可以阅读有关按键每个功能的信息。

于 2010-08-01T01:53:09.920 回答
1

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

my $value = $hash{name};

只是一个标量。

my $ref = \$hash{name};

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

于 2010-08-01T02:25:35.253 回答
0

试着把它想象成数据库表。有一个用户“表”/散列将用户 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 或更多)时才担心这个问题。

于 2015-03-16T15:31:40.307 回答