80

在 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
4

12 回答 12

99

这里还有两种方式,都非常简洁。

$arr1 = @(0) * 20
$arr2 = ,0 * 20
于 2008-10-24T15:40:20.080 回答
54

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

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

bool的默认值显然是false,因此这适用于您的情况。同样,如果您创建一个类型化的int[]数组,您将获得默认值 0。

我用来初始化数组的另一种很酷的方法是使用以下速记:

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

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

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

希望这有点帮助!

于 2008-10-22T18:14:22.027 回答
47

还有另一种选择:

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

如果 $arr 尚未定义,则此方法有效。

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

于 2008-10-22T16:46:49.240 回答
41

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

这里有很多创造性的答案,很多我在阅读这篇文章之前都不知道。对于小型阵列来说,一切都很好,但正如 n0rd 指出的那样,性能存在显着差异。

在这里,我使用 Measure-Command 来找出每次初始化需要多长时间。正如您可能猜到的那样,任何使用显式 PowerShell 循环的方法都比使用 .Net 构造函数或 PowerShell 运算符(将在 IL 或本机代码中编译)的方法慢。

概括

  • New-Object并且@(somevalue)*n速度很快(100k 个元素大约 20k 滴答声)。
  • 使用范围运算符创建数组n..m要慢 10 倍(200k 滴答声)。
  • 将 ArrayList 与该方法一起使用比基线(20M 滴答声)慢 1000 倍,就像使用or (又名, )Add()循环通过已经大小的数组一样。 for()ForEach-Objectforeach%
  • 附加+=是最糟糕的(仅 1000 个元素的 2M 滴答声)。

总的来说,我会说array * n是“最好的”,因为:

  • 它很快。
  • 您可以使用任何值,而不仅仅是类型的默认值。
  • 您可以创建重复值(为了说明,请在 powershell 提示符下键入:(1..10)*10 -join " "('one',2,3)*3
  • 简洁的语法。

唯一的缺点:

  • 不明显。如果你以前没有见过这个结构,那么它的作用就不清楚了。

但请记住,对于许多您希望将数组元素初始化为某个值的情况,强类型数组正是您所需要的。如果您将所有内容初始化为$false,那么该数组是否会保存除$falseor以外的任何内容$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

创建布尔数组比对象数组慢一点:

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 for array*n,@Scott Saad 和 Lee Desmond for New-Object,以及 @EBGreen for ArrayList。

感谢@n0rd 让我思考性能。

于 2015-01-18T21:55:54.160 回答
13
$array = 1..5 | foreach { $false }
于 2008-10-23T15:30:30.190 回答
11
$array = @()
for($i=0; $i -lt 5; $i++)
{
    $array += $i
}
于 2008-10-22T16:48:28.473 回答
11

这是另一个想法。您必须记住,它下面是 .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

Usingnew()有一个明显的优势:当您在 ISE 中编程并想要创建一个对象时,ISE 会为您提供所有参数组合及其类型的提示。你没有那个New-Object,你必须记住参数的类型和顺序。

ISE IntelliSense 用于新对象

于 2016-01-25T14:43:16.990 回答
7

我找到的解决方案是使用 New-Object cmdlet 来初始化适当大小的数组。

$array = new-object object[] 5 
for($i=0; $i -lt $array.Length;$i++)
{
    $array[$i] = $FALSE
}
于 2008-10-22T16:36:53.910 回答
6

如果我不知道前面的大小,我使用数组列表而不是数组。

$al = New-Object System.Collections.ArrayList
for($i=0; $i -lt 5; $i++)
{
    $al.Add($i)
}
于 2008-10-22T16:43:52.053 回答
1

这是另一种典型的方式:

$array = for($i = 0; $i -le 4; $i++) { $false }
于 2019-10-11T14:27:54.497 回答
0

或者试试这个想法。适用于 powershell 5.0+。

[bool[]]$tf=((,$False)*5)
于 2018-10-23T22:03:04.600 回答
0
$array = foreach($i in 1..5) { $false }
于 2021-09-14T13:55:33.443 回答