Wie kann ein Array in PowerShell am besten initialisiert werden?
Zum Beispiel der Code
$array = @()
for($i=0; $i -lt 5;$i++)
{
$array[$i] = $FALSE
}
erzeugt den Fehler
Array assignment failed because index '0' was out of range.
At H:\Software\PowerShell\TestArray.ps1:4 char:10
+ $array[$ <<<< i] = $FALSE
arrays
powershell
Eric Ness
quelle
quelle
Antworten:
Noch eine Alternative:
for ($i = 0; $i -lt 5; $i++) { $arr += @($false) }
Dieser funktioniert, wenn $ arr noch nicht definiert ist.
HINWEIS - Es gibt bessere (und leistungsfähigere) Möglichkeiten, dies zu tun. Ein Beispiel finden Sie unter https://stackoverflow.com/a/234060/4570 .
quelle
$i -lt 5
, müssen$i -lt 500000
Sie lange warten, bis es fertig ist.ArrayList
oder etwas Ähnliches. Es ist dann aber technisch gesehen kein Array.$arr = New-Object bool[] 5
Hier sind zwei weitere Möglichkeiten, beide sehr prägnant.
$arr1 = @(0) * 20 $arr2 = ,0 * 20
quelle
Sie können sich auch auf den Standardwert des Konstruktors verlassen, wenn Sie ein typisiertes Array erstellen möchten:
> $a = new-object bool[] 5 > $a False False False False False
Der Standardwert eines Bools ist anscheinend falsch, sodass dies in Ihrem Fall funktioniert. Wenn Sie ein typisiertes int [] -Array erstellen , erhalten Sie ebenfalls den Standardwert 0.
Eine andere coole Art, Arrays zu initialisieren, ist die folgende Kurzform:
> $a = ($false, $false, $false, $false, $false) > $a False False False False False
Oder wenn Sie einen Bereich initialisieren möchten, habe ich dies manchmal nützlich gefunden:
Hoffe das war etwas hilfreich!
quelle
$a = @(); $a += ...
$array = [int[]]::new(5)
. 2-dimensionale Anordnung:$array = [int[][]]::new(5,3)
Das ursprüngliche Beispiel gibt einen Fehler zurück, da das Array leer erstellt wurde. Anschließend versuchen Sie, auf das n-te Element zuzugreifen, um ihm einen Wert zuzuweisen.
Hier gibt es eine Reihe kreativer Antworten, viele, die ich vor dem Lesen dieses Beitrags nicht kannte. Alle sind für ein kleines Array in Ordnung, aber wie n0rd hervorhebt, gibt es signifikante Leistungsunterschiede.
Hier benutze ich Measure-Command, um herauszufinden, wie lange jede Initialisierung dauert. Wie Sie vielleicht erraten haben, ist jeder Ansatz, der eine explizite PowerShell-Schleife verwendet, langsamer als der, der .NET-Konstruktoren oder PowerShell-Operatoren verwendet (die in IL oder nativem Code kompiliert würden).
Zusammenfassung
New-Object
und@(somevalue)*n
sind schnell (ca. 20.000 Ticks für 100.000 Elemente).n..m
ist 10x langsamer (200.000 Ticks).Add()
Methode ist 1000-mal langsamer als die Basislinie (20 Millionen Ticks), ebenso wie das Durchlaufen eines bereits großen Arrays mitfor()
oderForEach-Object
(auch bekanntforeach
als%
).+=
ist das Schlimmste (2 Millionen Ticks für nur 1000 Elemente).Insgesamt würde ich sagen, Array * n ist "am besten", weil:
(1..10)*10 -join " "
oder('one',2,3)*3
)Einziger Nachteil:
Beachten Sie jedoch, dass in vielen Fällen, in denen Sie die Array-Elemente auf einen bestimmten Wert initialisieren möchten, ein stark typisiertes Array genau das ist, was Sie benötigen. Wenn Sie alles auf initialisieren
$false
, wird das Array dann jemals etwas anderes als$false
oder enthalten$true
? Wenn nicht, dannNew-Object type[] n
ist der "beste" Ansatz.Testen
Erstellen und skalieren Sie ein Standardarray und weisen Sie dann Werte zu:
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
Das Erstellen eines Booleschen Arrays ist etwas langsamer als ein Array von Object:
PS> Measure-Command -Expression {$a = New-Object bool[] 100000} | Format-List -Property "Ticks" Ticks : 130968
Es ist nicht offensichtlich, was dies bewirkt. In der Dokumentation zu New-Object heißt es lediglich, dass der zweite Parameter eine Argumentliste ist, die an den .Net-Objektkonstruktor übergeben wird. Bei Arrays ist der Parameter offensichtlich die gewünschte Größe.
Anhängen mit + =
PS> $a=@() PS> Measure-Command -Expression { for ($i=0; $i -lt 100000; $i++) {$a+=$false} } | Format-List -Property "Ticks"
Ich hatte es satt, darauf zu warten, also Strg + C dann:
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
So wie (6 * 3) konzeptionell (6 + 6 + 6) ähnlich ist, sollte ($ somearray * 3) das gleiche Ergebnis liefern wie ($ somearray + $ somearray + $ somearray). Bei Arrays ist + jedoch eher eine Verkettung als eine Addition.
Wenn $ array + = $ element langsam ist, können Sie erwarten, dass $ array * $ n auch langsam ist, aber es ist nicht:
PS> Measure-Command -Expression { $a = @($false) * 100000 } | Format-List -Property "Ticks" Ticks : 20131
So wie Java über eine StringBuilder-Klasse verfügt, um zu vermeiden, dass beim Anhängen mehrere Objekte erstellt werden, scheint PowerShell über eine ArrayList zu verfügen.
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
Bereichsoperator und
Where-Object
Schleife:PS> Measure-Command -Expression { $a = 1..100000 } | Format-List -Property "Ticks" Ticks : 239863 Measure-Command -Expression { $a | % {$false} } | Format-List -Property "Ticks" Ticks : 102298091
Anmerkungen:
$a=$null
) auf Null gesetzt .Danksagung
Vielen Dank an @ halr9000 für Array * n, @ Scott Saad und Lee Desmond für New-Object und @EBGreen für ArrayList.
Vielen Dank an @ n0rd, dass ich über Leistung nachgedacht habe.
quelle
$array = 1..5 | foreach { $false }
quelle
Hier ist eine andere Idee. Sie müssen sich daran erinnern, dass es sich um .NET handelt:
$arr = [System.Array]::CreateInstance([System.Object], 5) $arr.GetType() $arr.Length $arr = [Object[]]::new(5) $arr.GetType() $arr.Length
Ergebnis:
IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array 5 True True Object[] System.Array 5
Die Verwendung
new()
hat einen entscheidenden Vorteil: Wenn Sie in ISE programmieren und ein Objekt erstellen möchten, gibt Ihnen ISE einen Hinweis auf alle Paramer-Kombinationen und deren Typen. Sie haben das nicht mitNew-Object
, wo Sie sich die Arten und die Reihenfolge der Argumente merken müssen.quelle
::new()
ist in der Tat praktisch; Erwähnenswert ist, dass PSv5 + erforderlich ist.$array = @() for($i=0; $i -lt 5; $i++) { $array += $i }
quelle
Die Lösung, die ich gefunden habe, bestand darin, das Cmdlet New-Object zu verwenden, um ein Array mit der richtigen Größe zu initialisieren.
$array = new-object object[] 5 for($i=0; $i -lt $array.Length;$i++) { $array[$i] = $FALSE }
quelle
Wenn ich die Größe im Voraus nicht kenne, verwende ich eine Arrayliste anstelle eines Arrays.
$al = New-Object System.Collections.ArrayList for($i=0; $i -lt 5; $i++) { $al.Add($i) }
quelle
Oder versuchen Sie dies eine Idee. Funktioniert mit Powershell 5.0+.
[bool[]]$tf=((,$False)*5)
quelle
@(false) * 100000
ergab 91802 Zecken.Hier ist ein anderer typischer Weg:
$array = for($i = 0; $i -le 4; $i++) { $false }
quelle