我在Perl中对哈希进行排序。运行Perl脚本时遇到内存不足错误:
foreach $key (sort (keys(%hash))) {
....
}
如何对包含大量数据的哈希进行排序?
答案 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;
}
不可否认,这段代码非常难看,但它会将内存使用量降到最低。