如何对包含大量数据的Perl哈希进行排序?

时间:2010-05-18 04:01:09

标签: perl sorting hash

我在Perl中对哈希进行排序。运行Perl脚本时遇到内存不足错误:

foreach $key (sort (keys(%hash))) {
   ....
}

如何对包含大量数据的哈希进行排序?

3 个答案:

答案 0 :(得分:13)

sort keys %hash对于大%hash来说是低效的,在内存方面,它大致相当于:

my @keys = keys %hash;
@keys = sort @keys;

因为它必须在进行排序时将三个密钥副本保留在内存中(一个在散列中,一个在键列表中,一个在创建的排序列表中)。 foreach对迭代器的内存优化不适用。

由于哈希是如此之大,最好的选择是让它完全没有内存。将其粘贴在BerkeleyDB文件中。如果你想保持密钥的顺序,哈希不是最好的选择,树就是。我建议使用Berkeley BTree文件。树将有效地保持您的数据像数组一样排序,同时提供像哈希一样的快速查找。

以下是使用BerkeleyDB的示例。 DB_File更简单,更好地记录,但没有利用BerkeleyDB的现代功能。 YMMV。

use BerkeleyDB;

my $db  = tie my %hash, 'BerkeleyDB::Btree',
              -Filename => "your.db",
              -Compare  => sub { $_[1] cmp $_[0] },
              -Flags    => DB_CREATE;

-Compare说明了如何提供自己的排序功能。绑定的界面将是缓慢的。除非你需要它像哈希一样,否则使用对象接口。

答案 1 :(得分:0)

Perl FAQ有一些例子可以对哈希进行排序。请查看How do I sort a hash?,此处为A Fresh Look at Efficient Perl Sorting

答案 2 :(得分:0)

如果你的键是整数,数字或最小尺寸的字符串,你可以使用Sort :: Packed:

use Sort::Packed qw(sort_packed);

my $hash_size = keys %hash;
my $max_key_len = 4;  
my $packed_keys = '\0' x ($max_key_len * $hash_size);
my $ix = 0;
while (my ($key, $value) = each %hash) {
  my $key_len = length $k;
  $key_len <= $max_key_len or die "key $key is too big";
  substr($packed_keys, $ix, $key_len, $key);
  $ix += $max_key_len;
}

sort_packed("C$max_key_len", $packed_keys);

$ix = 0;
while ($ix < length $packed_keys) {
  my $key = substr($packed_keys, $ix, $max_key_len);
  $key =~ s/\0+$//;
  print "$key\n";
  $ix += $max_key_len;
}

不可否认,这段代码非常难看,但它会将内存使用量降到最低。