在具有相同键的哈希值的文档中是否可以保证具有相同的顺序?

时间:2012-10-04 09:18:23

标签: perl hash

对Perl中的哈希键顺序没有多少保证。在文档中是否有任何提及,我发现只要两个哈希使用完全相同的键,它们将完全相同的顺序?

短暂的测试似乎证实了这一点。即使我在分配给两个不同的哈希值之间为内部密钥表生成一些额外的密钥,它们的密钥也会以相同的顺序返回:

my %aaa;
my %bbb;
my %ccc;
my %ddd;

@aaa{qw(a b c d e f g h i j k l)}=();
# Let's see if generating more keys for internal table matters
@ccc{qw(m n o p q r s t u v w x)}=();
@bbb{qw(a b c d e f g h i j k l)}=();
# Just to test if different insertion order matters
@ddd{qw(l k c d e f g h i j a)}=(); $ddd{b} = ();

print keys %aaa, "\n";
print keys %bbb, "\n";
print keys %ddd, "\n";

但是我不会依赖于udocumented行为,只有在文档中可以轻松找到的事实是keysvalueseach只要哈希就会使用相同的顺序没有修改。

6 个答案:

答案 0 :(得分:13)

来自perlsec:

  

Perl从未保证任何哈希键的排序,以及   在Perl的生命周期中,排序已经多次改变   5.此外,散列键的排序一直并且继续受插入顺序的影响。

http://perldoc.perl.org/perlsec.html

答案 1 :(得分:7)

较长时间的测试反驳。

因此,使用同一组键的不同哈希值将始终具有相同的顺序。对我来说,下面的程序演示了两个带键qw(a b c d e f)的哈希值可能在顺序上有所不同:

v5.16.0
%h1: ecabdf
%h2: eadcbf

程序:

#!/usr/bin/env perl

use strict;
use warnings;
use feature qw(say);

# http://stackoverflow.com/q/12724071/132382

use constant KEYS => qw(a b c d e f);

my %h1 = map { $_ => undef } KEYS;
my %h2 = map { $_ => undef } KEYS;

delete @h2{'b', 'd', 'f'};
@h2{'x', 'y', 'z'} = ();
@h2{'b', 'd', 'f'} = ();
delete @h2{'x', 'y', 'z'};

say $^V;
say '%h1: ', keys(%h1);
say '%h2: ', keys(%h2);

更新

以下是一个简单的演示,单独的插入顺序很重要:

$ perl -MList::Util=shuffle -E \
> '@keys = ('a'..'z'); @h1{@keys} = @h2{shuffle @keys} = ();
> say keys(%$_) for (\%h1, \%h2)'
wraxdjyukhgftienvmslcpqbzo
warxdjyukhgftienmvslpcqbzo
#^^             ^^  ^^
#||             ||  ||

答案 2 :(得分:5)

特别保证这是不可靠的。

完整查看perlsec的{​​{3}}部分。尽管存在令人遗憾的不连贯性,但它表示

  • 在5.8.1中,订单保证每次都是随机的。
  • 在5.8.2及更高版本中,顺序将是相同的,除非Perl检测到病态行为(具体地说,一系列密钥将全部散列到少量桶,导致散列性能受损)。在这些情况下,“函数受到伪随机种子的干扰”。

文档保证订购总是一样的;事实上,它明确指出它在病理情况下是可预测的。如果在将来的版本中更改散列函数,以前没有生成退化散列的数据现在可能会这样做,然后会受到随机扰动的影响。

所以需要注意的是,如果你没有使用5.8.1,可能你会获得相同的订单,而也许它会在你使用时不会改变更新你的Perl,但它可能。如果您使用的是5.8.1,那么随机订单保证

如果您想要一个可靠的订单,请使用其中一个提供具有保证密钥顺序的散列的CPAN类 - Algorithmic Complexity AttacksTie::Hash::Indexed - 或者只是对您的密钥进行排序。如果你的哈希值少于几千个键,你可能不会注意到明显的差异。如果它不止于此,也许你应该考虑一个比较重的解决方案,比如数据库。

编辑:只是为了让它变得更有趣,Tie::IxHash

答案 3 :(得分:4)

这是一个比@pilcrow's短的反例(显然我在第一次看这个问题时错过了他的答案):

#!/usr/bin/env perl

use strict; use warnings;

my @hashes = (
    { map { $_ => rand } 'a' .. 'z' },
    { map { $_ => rand } 'a' .. 'd', 'f' .. 'z' }
);

delete $hashes[0]{e};

print "@{[ keys %$_ ]}\n" for @hashes;

输出:

C:\temp> t
w r a x d j y u k h g f t i n v m s l c p q b z o
w r a x d j y u k h g f t i n v m s l c p b q z o

答案 4 :(得分:3)

perldoc -f keys有关于订购的一些信息:

  

散列的键以明显随机的顺序返回。实际的随机顺序在未来的Perl版本中可能会发生变化,但保证与值或每个函数产生的顺序相同(假设散列未被修改)。 从Perl 5.8.1开始,出于安全原因,即使在不同的Perl运行之间排序也可能不同(请参阅perlsec中的算法复杂性攻击)。

所以唯一的保证就是不保证订购。

答案 5 :(得分:0)

由于至少5.18,perldoc perlsec中明确提到了以下内容:

  

keysvalueseach按每个哈希随机顺序返回项目。   通过插入修改哈希将改变其迭代顺序   散列。

  

Perl从未保证任何哈希键的排序,以及   在Perl的生命周期中,排序已经多次改变   5.此外,散列键的排序一直并且继续受到插入顺序和所做更改历史的影响   哈希的生命周期。

因此,明确不保证具有相同键集的两个哈希以相同的顺序迭代。