PowerShell数组初始化


77

在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

1
告诉我们您要完成的任务,也许我们将能够为您提供更好的“惯用PowerShell”答案。我从来不需要在PowerShell中新建一个数组。
彼得·希尔

我没有看到有人特别提到数组是不可变的。创建后,便无法修改。
AdminOfThings

Answers:


47

另一个选择:

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

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

注意-有更好的(且性能更高)的方法可以执行此操作...请参阅以下https://stackoverflow.com/a/234060/4570


13
这太慢了。由于无法调整.NET数组的大小,因此从本质上来说,这会为新数组分配额外的空间来存储新项目并复制数据,因此,如果在上面的代码段中更改$i -lt 5$i -lt 500000,则需要等待很长时间才能完成。
2015年

脱壳时,我一直都在使用这种方法。baaad的习惯是编写必须坚持的脚本。这是完全不必要的O(n ^ 2)时间。即使在炮击时,我也必须停止并有时以正确的方式进行操作。这是超级方便。
Nacht'3

老实说,我通常根本不使用这种方法,而是通常使用ArrayList或类似的方法。从技术上讲,它不是数组。
David Mohundro

6
这不是正确的方法。由于PowerShell处理数组的方式,它恰好起作用。使用这个:$arr = New-Object bool[] 5
marsze

94

这是另外两种方式,两种方式都非常简洁。

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

很好,我今天早上想弄清楚这一点,我认为您提供了最简洁的初始化数组的方法。
克里斯·萨顿

谢谢。我的博客上还有更多信息,这个话题是2007
article/

我一定很胖。有人可以解释这是做什么的,* 20的作用是什么?20在其他人的答案或问题中没有出现。
路加·普普利特

就像Powershell阵列采用乘法运算符一样,该运算符仅对其自身进行多次复制。很酷
Nacht'3

@ halr9000您的链接已损坏。这就是为什么我们发现将相关信息放入答案本身的原因。:)
Cullub

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)   
> $ a
1个
2
3
4
5

希望这对您有所帮助!


1
或:$a = @(); $a += ...
marsze

1
我发现可读性更强的新对象(PS 5.0)替代品(ISE中提供了智能感应):1维数组:$array = [int[]]::new(5)。二维数组:$array = [int[][]]::new(5,3)
Petru Zaharia,

39

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

这里有许多创造性的答案,许多我在阅读本文之前都不知道。对于一个小的数组,一切都很好,但是正如n0rd指出的那样,性能存在显着差异。

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

概要

  • New-Object并且@(somevalue)*n速度很快(100k元素大约需要20k滴答)。
  • 使用范围运算符创建数组的n..m速度要慢10倍(200k滴答)。
  • 与该Add()方法一起使用ArrayList的速度比基线(2000万个刻度)慢1000倍,使用for()ForEach-Object(aka foreach%)遍历一个已经大小的数组也是如此。
  • 追加+=是最差的(仅1000个元素就有200万个滴答声)。

总的来说,我会说数组* 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用于array * n,@Scott Saad和Lee Desmond用于New-Object,以及@EBGreen用于ArrayList。

感谢@ n0rd让我思考性能。


很棒的故障。我有一个很好的脚本,该脚本使用$ _。split(''[[.0.1)]检查IIS日志中的日期和时间,但是当处理时间以指数方式查看100,000条日志时,更改为$ _。indexof方法条目。
Kirt Carson


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

使用new()具有一个明显的优势:当您在ISE中进行编程并想要创建一个对象时,ISE会提示您所有参数组合及其类型。您无需使用New-Object,您必须记住参数的类型和顺序。

ISE IntelliSense用于新对象


1
::new()确实很方便;值得一提的是,它需要PSv5 +。
mklement0 '18年

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

7

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

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

5

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

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

为什么用它代替数组和+ =?
瑞安·费舍尔

没有特别的理由。只是来自限制性更强的语言的习惯,需要预先定义数组大小。另外,我喜欢arraylists中内置的额外功能。
EBGreen

另外,如果您注意到,我也提供了数组+ =答案。这一切都是在3年前完成的,然后才真正确定SO的工作方式。今天,我将把这两种方法结合在一起。
EBGreen

3
@RyanFisher数组无法调整大小,因此使用+ =将在每次调用+ =时制作整个数组的完整副本。这意味着+ =是O(n),而ArrayList.Add()是O(1)。以我的经验,如果您要对数组进行任何远程管理,那么使用ArrayList会更好。
培根Bits 2013年

0

或尝试这个主意。适用于Powershell 5.0+。

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

用100000个元素(而不是5个)测量此命令会产生526247个滴答声。测量@(false) * 100000导致91802个滴答。
AdminOfThings

0

这是另一种典型的方式:

$array = for($i = 0; $i -le 4; $i++) { $false }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.