PowerShell数组初始化

Eri*_*ess 71 arrays powershell

在PowerShell中初始化数组的最佳方法是什么?

例如,代码

$array = @()
for($i=0; $i -lt 5;$i++)
{
    $array[$i] = $FALSE
}
Run Code Online (Sandbox Code Playgroud)

生成错误

Array assignment failed because index '0' was out of range.
At H:\Software\PowerShell\TestArray.ps1:4 char:10
+         $array[$ <<<< i] = $FALSE
Run Code Online (Sandbox Code Playgroud)

hal*_*000 89

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

$arr1 = @(0) * 20
$arr2 = ,0 * 20
Run Code Online (Sandbox Code Playgroud)


Sco*_*aad 51

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

> $a = new-object bool[] 5
> $a
False
False
False
False
False
Run Code Online (Sandbox Code Playgroud)

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

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

> $a = ($false, $false, $false, $false, $false)
> $a
False
False
False
False
False
Run Code Online (Sandbox Code Playgroud)

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

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

希望这有点帮助!

  • 我发现 New-Object 的更新(PS 5.0)替代品更具可读性(ISE 中提供 Intellisense):一维数组:`$array = [int[]]::new(5)`。二维数组:`$array = [int[][]]::new(5,3)` (2认同)

Dav*_*dro 43

另一种选择:

for ($i = 0; $i -lt 5; $i++) 
{ 
  $arr += @($false) 
}
Run Code Online (Sandbox Code Playgroud)

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

注意 - 有更好(更高性能)的方法来执行此操作...请参阅下面的/sf/answers/16384231/作为示例.

  • 这太慢了.由于.NET数组无法调整大小,因此实际上为新项目分配了新数组,并为新项目和副本数据添加了空间,因此如果在上面的代码片段中将`$ i -lt 5`更改为`$ i -lt 500000`,那么等待时间一直等到它完成. (12认同)
  • 这不是正确的方法.由于PowerShell处理数组的方式,它恰好正常工作.使用此:`$ arr = New-Object bool [] 5` (5认同)

Cel*_*Man 37

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

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

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

摘要

  • New-Object并且@(somevalue)*n很快(对于100k元素,大约20k刻度).
  • 使用范围运算符创建数组的n..m速度要慢10倍(200k刻度).
  • 使用与所述一个ArrayList Add()方法比基线(20M蜱)慢1000倍,如使用通过一个已经大小的数组循环for()ForEach-Object(又名foreach,%).
  • 追加+=是最差的(仅1000个元素的2M滴答).

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

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

唯一的缺点:

  • 非显而易见的.如果您之前没有看过这个构造,那么它的作用并不明显.

但请记住,对于您希望将数组元素初始化为某个值的许多情况,强类型数组正是您所需要的.如果你正在初始化所有东西$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
Run Code Online (Sandbox Code Playgroud)

创建一个Boolean数组比Object的数组慢一点:

PS> Measure-Command -Expression {$a = New-Object bool[] 100000} | Format-List -Property "Ticks"
Ticks : 130968
Run Code Online (Sandbox Code Playgroud)

这并不明显,New-Object的文档只是说第二个参数是一个参数列表,它被传递给.Net对象构造函数.在阵列的情况下,参数显然是所需的大小.

附加+ =

PS> $a=@()
PS> Measure-Command -Expression { for ($i=0; $i -lt 100000; $i++) {$a+=$false} } | Format-List -Property "Ticks"
Run Code Online (Sandbox Code Playgroud)

我厌倦了等待完成,所以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
Run Code Online (Sandbox Code Playgroud)

正如(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
Run Code Online (Sandbox Code Playgroud)

就像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
Run Code Online (Sandbox Code Playgroud)

范围运算符和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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我在每次运行($a=$null)之间调整了变量.
  • 使用Atom处理器在平板电脑上进行测试; 你可能会在其他机器上看到更快的速度.[编辑:在桌面计算机上快两倍.]
  • 当我尝试多次运行时,有一些变化.寻找数量级而不是确切的数字.
  • 使用Windows 8中的PowerShell 3.0进行测试.

致谢

感谢@ halr9000 for array*n,@ Scott Saad和Lee Desmond为New-Object,@ EBGreen为ArrayList.

感谢@ n0rd让我考虑性能.


Pet*_*ale 12

$array = 1..5 | foreach { $false }
Run Code Online (Sandbox Code Playgroud)


EBG*_*een 9

$array = @()
for($i=0; $i -lt 5; $i++)
{
    $array += $i
}
Run Code Online (Sandbox Code Playgroud)


Ada*_*ski 9

这是另一个想法.你必须记住,它是.NET下面的:

$arr = [System.Array]::CreateInstance([System.Object], 5)
$arr.GetType()
$arr.Length

$arr = [Object[]]::new(5)
$arr.GetType()
$arr.Length
Run Code Online (Sandbox Code Playgroud)

结果:

IsPublic IsSerial Name                                     BaseType                                                                                               
-------- -------- ----                                     --------                                                                                               
True     True     Object[]                                 System.Array                                                                                           
5
True     True     Object[]                                 System.Array                                                                                           
5
Run Code Online (Sandbox Code Playgroud)

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

ISE IntelliSense用于新对象


Eri*_*ess 7

我发现的解决方案是使用New-Object cmdlet初始化适当大小的数组.

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


EBG*_*een 5

如果我不知道前面的大小,我使用arraylist而不是数组.

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

  • @RyanFisher Arrays无法调整大小,因此每次调用+ =时,使用+ =将生成整个数组的完整副本.这意味着+ =是O(n),而ArrayList.Add()是O(1).根据我的经验,如果你正在使用数组远程fiddley做任何事情,你最好使用ArrayList. (2认同)