假设你有一个数组@a = qw/ a b c d/;
和一个哈希%a=('a'=>1,'b'=>1,'c'=>1,'d'=>1);
除了在类似于必须遍历所有值的情况下(例如
for (@a){
....
如果你选择使用哈希表,那么你需要使用keys %a
来进行操作,对吗?因为在哈希表中查找特定值的效率总是比在数组中高,是这样吗?
有许多 emergent properties。主要的是:
数组是有序的值列表,它们可以包含重复的值。
@array = qw(a b c a);
哈希表是一种将唯一键和可重复值进行映射的数据结构。哈希表是无序的,这意味着键以看似随机的顺序输出,而不是它们输入的顺序。
%hash = (a => 1, b => 2, c => 3);
哈希也可以作为集合使用,仅关注键时。集合是无序的,仅包含唯一的“值”(哈希的键)。
%set = (a => undef, b => undef, c => undef);
根据您的数据和算法,选择使用哪种取决于情况。如果顺序很重要(特别是如果无法排序以得出顺序)或者可能存在重复值,则使用数组。如果值必须唯一且不关心顺序,则使用集合(即使用哈希作为集合)。当唯一性很重要,而顺序不重要(或很容易进行排序),并且查找是基于任意值而不是整数时,请使用哈希。
您可以通过引用将数组和哈希组合在一起,创建任意复杂的数据结构。
@aoa = ([1, 2, 3], [4, 5, 6]); # array of arrays ("2D" array)
%hoh = (a => { x => 1 }, b => { x => 2 }); # hash of hashes
@aoh = ({a => 1, b => 2}, {a => 3, b => 4}); # array of hashes
%hoa = (a => [1, 2], b => [3, 4]); # hash of arrays
...etc.
$a[2] = "a"; $a[0] = "b"; $a[1] = "c"; print values(@a);
和 $h{2} = "a"; $h{0} = "b"; $h{1} = "c"; print values(%h);
都无法给出 abc
。这些值仅仅是被键控的,你可以对数组和哈希表进行键排序。实际上的区别在于对于数组来说,排序键更加高效。(在你提到push
和pop
之前,请记住你同样可以为哈希表创建可用的push
和pop
函数。) - ikegami@a
总是会打印为bca
。它们是按索引排序的。我从未说过它们的排序是基于插入的。 - Michael Carmanuse strict;
use warnings;
my %hash;
my $n = 1000;
for (1 .. 10) {
$hash{$n} = 1;
$n *= 1000;
}
然后我们查询它,寻找是十的幂的键。当然,将整数乘以十最简单的方法是添加一个零,因此写成这样是可以的
my $m = '1';
for (1 .. 100) {
print $m, "\n" if $hash{$m};
$m .= 0;
}
它的输出结果为
1000
1000000
1000000000
1000000000000
1000000000000000
1000000000000000000
use Data::Dump;
dd \%hash;
这将输出
{
"1000" => 1,
"1000000" => 1,
"1000000000" => 1,
"1000000000000" => 1,
"1000000000000000" => 1,
"1000000000000000000" => 1,
"1e+021" => 1,
"1e+024" => 1,
"1e+027" => 1,
"1e+030" => 1,
}
因此哈希表并不使用我们想象中的键。它会以一种字符串化数字的方式来哈希,模仿它是愚蠢的。
举一个稍微更实际的例子,假设我们有一些圆形,并且想按面积将它们收集到集合中。显然可以使用面积作为哈希键,就像这个程序创建了 100,000 个随机整数直径不超过 1800 万的圆形。
use strict;
use warnings;
use 5.010;
package Circle;
use Math::Trig 'pi';
sub new {
my $class = shift;
my $self = { radius => shift };
bless $self, $class;
}
sub area {
my $self = shift;
my $radius = $self->{radius};
pi * $radius * $radius;
}
package main;
my %circles;
for (1 .. 100_000) {
my $circle = Circle->new(int rand 18_000_000);
push @{ $circles{$circle->area} }, $circle;
}
say scalar grep /e/, keys %circles;
这段代码(当然是随机的)
861
所以,如果我们将数字作为哈希索引指定,实际上并没有一种简洁的方法来确定 Perl 将使用哪个字符串。
@array
是一个值的有序列表($v1, $v2, ...)
,可以通过整数(正数和负数)访问,而%hash
是一个无序的'key => value'对列表(k1 => $v1, k2 => $v2, ...)
,可以通过字符串访问。
$x{$i}
是否真的比$x[$i]
快。 (其实不是这样的。) 这与Tyler的问题无关。 - ikegami