PowerShellarrays初始化
在PowerShell中初始化数组的最佳方法是什么?
例如,代码
$array = @() for($i=0; $i -lt 5;$i++) { $array[$i] = $FALSE }
生成错误
Array assignment failed because index '0' was out of range. At H:\Software\PowerShell\TestArray.ps1:4 char:10 + $array[$ <<<< i] = $FALSE
还有另一种select:
for ($i = 0; $i -lt 5; $i++) { $arr += @($false) }
如果$ arr尚未定义,这个工作。
这里有两种方法,都非常简洁。
$arr1 = @(0) * 20 $arr2 = ,0 * 20
如果你想创build一个types数组,你也可以依赖构造函数的默认值 :
> $a = new-object bool[] 5 > $a False False False False False
布尔的默认值显然是错误的,所以这在你的情况下工作。 同样,如果你创build一个types化的int []数组,你将得到默认值0。
我用来初始化数组的另一个很酷的方法是用下面的简写:
> $a = ($false, $false, $false, $false, $false) > $a False False False False False
或者,如果你想要初始化一个范围,我有时会发现这是有用的:
> $ a =(1..5) > $ a 1 2 3 4 五
希望这有些帮助!
最初的例子返回一个错误,因为数组被创build为空,那么您尝试访问第n个元素来为其分配一个值。
这里有很多有创意的答案,很多我在阅读这篇文章之前都不知道。 对于一个小arrays来说都是好的,但是正如所指出的那样,在性能上存在显着的差异。
在这里我使用Measure-Command来确定每个初始化需要多长时间。 正如你可能猜到的那样,任何使用显式PowerShell循环的方法比那些使用.Net构造函数或PowerShell操作符(可以用IL或本地代码编译)的方法要慢。
概要
-
New-Object
和@(somevalue)*n
是快速的(对于100k元素,大约20k)。 - 使用范围运算符
n..m
创build一个数组要慢10倍(20万次)。 - 与使用
Add()
方法的ArrayList相比,使用for()
或ForEach-Object
(又名foreach
,%
)循环已经大小的数组的速度是基线(20M ticks)的1000倍。 - 用
+=
追加是最差的(只有1000个元素,为2M)。
总的来说,我会说arrays * n是“最好的”,因为:
- 它很快。
- 您可以使用任何值,而不仅仅是types的默认值。
-
(1..10)*10 -join " "
或('one',2,3)*3
)可以创build重复的值 - Terse语法。
唯一的缺点:
- 非显而易见的。 如果你之前没有看过这个构造,那么它的function并不明显。
但请记住,对于很多情况下,如果您想将数组元素初始化为某个值,那么强types数组正是您所需要的。 如果你把所有东西初始化为$false
,那么这个数组是否会持有除$false
或$true
以外的任何东西? 如果不是,那么New-Object type[] n
是“最好”的方法。
testing
创build并设置默认数组的大小,然后分配值:
PS> Measure-Command -Expression {$a = new-object object[] 100000} | Format-List -Property "Ticks" Ticks : 20039 PS> Measure-Command -Expression {for($i=0; $i -lt $a.Length;$i++) {$a[$i] = $false}} | Format-List -Property "Ticks" Ticks : 28866028
创build一个布尔数组比Object和Object数组慢一点:
PS> Measure-Command -Expression {$a = New-Object bool[] 100000} | Format-List -Property "Ticks" Ticks : 130968
这并不明显,New-Object的文档只是说第二个参数是一个传递给.Net对象构造函数的参数列表。 在数组的情况下,参数显然是所需的大小。
附加+ =
PS> $a=@() PS> Measure-Command -Expression { for ($i=0; $i -lt 100000; $i++) {$a+=$false} } | Format-List -Property "Ticks"
我厌倦了等待完成,所以按Ctrl + C然后:
PS> $a=@() PS> Measure-Command -Expression { for ($i=0; $i -lt 100; $i++) {$a+=$false} } | Format-List -Property "Ticks" Ticks : 147663 PS> $a=@() PS> Measure-Command -Expression { for ($i=0; $i -lt 1000; $i++) {$a+=$false} } | Format-List -Property "Ticks" Ticks : 2194398
就像(6 * 3)在概念上与(6 + 6 + 6)类似,所以($ somearray * 3)应该给出与($ somearray + $ somearray + $ somearray)相同的结果。 但是对于数组,+是串联而不是加法。
如果$ array + = $元素很慢,你可能会希望$ array * $ n也很慢,但不是:
PS> Measure-Command -Expression { $a = @($false) * 100000 } | Format-List -Property "Ticks" Ticks : 20131
就像Java有一个StringBuilder类,以避免追加时创build多个对象,所以看起来PowerShell有一个ArrayList。
PS> $al = New-Object System.Collections.ArrayList PS> Measure-Command -Expression { for($i=0; $i -lt 1000; $i++) {$al.Add($false)} } | Format-List -Property "Ticks" Ticks : 447133 PS> $al = New-Object System.Collections.ArrayList PS> Measure-Command -Expression { for($i=0; $i -lt 10000; $i++) {$al.Add($false)} } | Format-List -Property "Ticks" Ticks : 2097498 PS> $al = New-Object System.Collections.ArrayList PS> Measure-Command -Expression { for($i=0; $i -lt 100000; $i++) {$al.Add($false)} } | Format-List -Property "Ticks" Ticks : 19866894
Range运算符和Where-Object
循环:
PS> Measure-Command -Expression { $a = 1..100000 } | Format-List -Property "Ticks" Ticks : 239863 Measure-Command -Expression { $a | % {$false} } | Format-List -Property "Ticks" Ticks : 102298091
笔记:
- 我在每次运行之间消除了variables(
$a=$null
)。 - Atom处理器在平板电脑上进行testing; 你可能会在其他机器上看到更快的速度。 [编辑:在台式机上快两倍]
- 当我尝试多次运行时,有一些变化。 寻找数量级,而不是确切的数字。
- 在Windows 8中使用PowerShell 3.0进行testing。
致谢
感谢@ halr9000的数组* n,@Scott Saad和Lee Desmond的New-Object,以及@EBGreen的ArrayList。
感谢@ n0rd让我思考性能。
$array = 1..5 | foreach { $false }
$array = @() for($i=0; $i -lt 5; $i++) { $array += $i }
我find的解决scheme是使用New-Object cmdlet来初始化一个适当大小的数组。
$array = new-object object[] 5 for($i=0; $i -lt $array.Length;$i++) { $array[$i] = $FALSE }
这是另一个想法。 你必须记住,它是下面的.NET:
$arr = [System.Array]::CreateInstance([System.Object], 5) $arr.GetType() $arr.Length $arr = [Object[]]::new(5) $arr.GetType() $arr.Length
结果:
IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array 5 True True Object[] System.Array 5
使用new()
有一个明显的优点:当你在ISE中编程并且想要创build一个对象时,ISE会给你所有的paramer组合及其types的提示。 你没有New-Object
,你必须记住参数的types和顺序。
如果我不知道大小,我使用arraylist而不是数组。
$al = New-Object System.Collections.ArrayList for($i=0; $i -lt 5; $i++) { $al.Add($i) }