PowerShell 数组初始化

在 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
231025 次浏览

我找到的解决方案是使用 New-Objectcmdlet 初始化一个大小适当的数组。

$array = new-object object[] 5
for($i=0; $i -lt $array.Length;$i++)
{
$array[$i] = $FALSE
}

如果预先不知道大小,我会使用数组列表而不是数组。

$al = New-Object System.Collections.ArrayList
for($i=0; $i -lt 5; $i++)
{
$al.Add($i)
}

还有一种选择:

for ($i = 0; $i -lt 5; $i++)
{
$arr += @($false)
}

如果尚未定义 $arr,则此方法可以工作。

注意 -有更好的(和更高性能的)方法可以做到这一点... 参见下面的 https://stackoverflow.com/a/234060/4570作为一个例子。

$array = @()
for($i=0; $i -lt 5; $i++)
{
$array += $i
}

如果希望创建类型化数组,还可以依赖于 构造函数的默认值:

> $a = new-object bool[] 5
> $a
False
False
False
False
False

布尔的默认值显然是 假的,因此这在您的情况下是可行的。同样,如果创建类型化的 Int []数组,将获得默认值0。

我用来初始化数组的另一个很酷的方法是使用以下简写:

> $a = ($false, $false, $false, $false, $false)
> $a
False
False
False
False
False

或者,如果你想初始化一个范围,我有时会发现这个很有用:

> $a = (1..5)
> $a
1
2
3
4
5

希望这对你有所帮助!

$array = 1..5 | foreach { $false }

还有两种方法,都很简洁。

$arr1 = @(0) * 20
$arr2 = ,0 * 20

原始示例返回一个错误,因为数组被创建为空,然后尝试访问第 n 个元素以为其赋值。

这里有很多富有创造性的答案,很多是我在读这篇文章之前不知道的。对于小数组来说,所有这些都很好,但是正如 n0rd 指出的那样,在性能方面存在显著差异。

在这里,我使用措施-命令来找出每个初始化需要多长时间。正如您可能猜测的那样,任何使用显式 PowerShell 循环的方法都比使用。Net 构造函数或 PowerShell 操作符(将用 IL 或本机代码编译)。

摘要

  • New-Object@(somevalue)*n是快速的(100k 元素大约20k 的节拍)。
  • 使用范围运算符 n..m创建一个数组的速度要慢10倍(200k 的滴答声)。
  • 使用带有 Add()方法的 ArrayList 比基线慢1000倍(2000万个节拍) ,就像使用 for()ForEach-Object(又名 foreach%)在已经大小的数组中循环一样。
  • 附加 +=是最糟糕的(只有1000个元素需要200个节拍)。

总的来说,我认为 阵列 * < em > n 是“最好的”,因为:

  • 很快。
  • 您可以使用任何值,而不仅仅是类型的默认值。
  • 您可以创建重复的值(例如,在 powershell 提示符 (1..10)*10 -join " "('one',2,3)*3中键入这个值)
  • 语法简洁。

唯一的缺点是:

  • 不明显。如果您以前没有见过这个结构,那么它的作用是不明显的。

但是请记住,在许多情况下,您希望将数组元素初始化为某个值,那么强类型数组正是您所需要的。如果要将所有内容初始化为 $false,那么数组是否会保存除 $false$true以外的内容?如果没有,那么 New-Object type[] n是“最好”的方法。

测试

创建默认数组并调整其大小,然后分配值:

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

创建一个 Boolean 数组比 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 + = $element 很慢,你可能会认为 $array * $n 也很慢,但事实并非如此:

PS> Measure-Command -Expression { $a = @($false) * 100000 } | Format-List -Property "Ticks"
Ticks : 20131

就像 Java 有一个 StringBuilder 类来避免在附加时创建多个对象一样,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

范围操作符,和 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

备注:

  • 每次运行之间变量为空($a=$null)。
  • 测试是在使用 Atom 处理器的平板电脑上进行的; 您可能会在其他机器上看到更快的速度。[编辑: 桌面电脑的速度大约是普通电脑的两倍。]
  • 当我尝试多次运行时,有相当多的变化。寻找数量级而不是精确的数字。
  • 在 Windows 8中使用 PowerShell 3.0进行测试。

致谢

感谢@halr9000表示 array * n,@Scott Saad 和 Lee Desmond 表示 New-Object,@EBGreen 表示 ArrayList。

感谢@n0rd 让我思考性能。

还有另外一个想法。你必须记住,它的底层是.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 中编程并希望创建对象时,ISE 将提示您所有的参数组合及其类型。在 New-Object中没有这种功能,在 New-Object中,必须记住参数的类型和顺序。

ISE IntelliSense for new object

或者试试这个主意,可以用 Powershell 5.0 + 。

[bool[]]$tf=((,$False)*5)

下面是另一种典型的方式:

$array = for($i = 0; $i -le 4; $i++) { $false }
$array = foreach($i in 1..5) { $false }