web-dev-qa-db-fra.com

Initialisation du tableau PowerShell

Quel est le meilleur moyen d'initialiser un tableau dans PowerShell?

Par exemple, le code

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

génère l'erreur

Array assignment failed because index '0' was out of range.
At H:\Software\PowerShell\TestArray.ps1:4 char:10
+         $array[$ <<<< i] = $FALSE
65
Eric Ness

Encore une autre alternative:

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

Celui-ci fonctionne si $ arr n'est pas encore défini.

NOTE - il existe des méthodes plus performantes (et plus performantes) ... voir https://stackoverflow.com/a/234060/4570 ci-dessous.

41
David Mohundro

Voici deux autres moyens très concis.

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

Vous pouvez également vous fier à la valeur default du constructeur si vous souhaitez créer un tableau typé:

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

La valeur par défaut de bool est apparemment false donc cela fonctionne dans votre cas. De même, si vous créez un tableau int [] typé, vous obtiendrez la valeur par défaut 0.

Une autre façon intéressante d’initialiser des tableaux consiste à utiliser le raccourci suivant:

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

Ou, si vous le souhaitez, vous souhaitez initialiser une plage, j'ai parfois trouvé cela utile:

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

J'espère que c'était un peu utile!

48
Scott Saad

L'exemple d'origine renvoie une erreur car le tableau est créé vide, puis vous essayez d'accéder au nième élément pour lui attribuer une valeur. 

Il y a un certain nombre de réponses créatives ici, beaucoup que je ne connaissais pas avant de lire ce post. Tout va bien pour un petit tableau, mais comme le souligne n0rd, il existe des différences de performances significatives. 

Ici, j'utilise Measure-Command pour savoir combien de temps prend chaque initialisation. Comme vous pouvez le deviner, toute approche utilisant une boucle PowerShell explicite est plus lente que celles qui utilisent des constructeurs .Net ou des opérateurs PowerShell (qui seraient compilés en langage IL ou en code natif).

Résumé

  • New-Object et @(somevalue)*n sont rapides (environ 20 000 ticks pour 100 000 éléments). 
  • La création d'un tableau avec l'opérateur de plage n..m est 10 fois plus lente (200 000 ticks). 
  • L'utilisation d'une ArrayList avec la méthode Add() est 1000 fois plus lente que la ligne de base (20 millions de ticks), tout comme une boucle dans un tableau déjà dimensionné utilisant for() ou ForEach-Object (a.k.a. foreach, %). 
  • Ajouter avec += est le pire (2 millions de ticks pour seulement 1000 éléments). 

Globalement, je dirais quearray * nest "meilleur" parce que: 

  • C'est rapide.
  • Vous pouvez utiliser n'importe quelle valeur, pas seulement la valeur par défaut pour le type.
  • Vous pouvez créer des valeurs répétitives (pour illustrer, tapez ceci à l’invite powershell: (1..10)*10 -join " " ou ('one',2,3)*3)
  • Syntaxe laconique. 

Le seul inconvénient: 

  • Pas évident. Si vous n'avez jamais vu cette construction auparavant, son utilité n'est pas évidente. 

Mais gardez à l'esprit que dans de nombreux cas où vous souhaitez initialiser les éléments du tableau à une valeur quelconque, un tableau fortement typé correspond exactement à ce dont vous avez besoin. Si vous initialisez tout à $false, le tableau contiendra-t-il autre chose que $false ou $true? Si ce n'est pas le cas, New-Object type[] n est la "meilleure" approche. 

Essai

Créez et redimensionnez un tableau par défaut, puis affectez des valeurs: 

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

Créer un tableau de Boolean est un peu plus lent qu'un tableau d'Object: 

PS> Measure-Command -Expression {$a = New-Object bool[] 100000} | Format-List -Property "Ticks"
Ticks : 130968

Ce n'est pas évident, la documentation de New-Object indique simplement que le second paramètre est une liste d'arguments transmise au constructeur d'objet .Net. Dans le cas des tableaux, le paramètre est évidemment la taille souhaitée. 

Ajout avec + =

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

J'en avais marre d'attendre que ça se termine, alors ctrl + c alors: 

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

Tout comme (6 * 3) est conceptuellement similaire à (6 + 6 + 6), donc ($ somearray * 3) doit donner le même résultat que ($ somearray + $ somearray + $ somearray). Mais avec les tableaux, + est la concaténation plutôt que l'addition. 

Si $ array + = $ element est lent, vous pouvez vous attendre à ce que $ array * $ n soit également lent, mais ce n'est pas: 

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

Tout comme Java a une classe StringBuilder pour éviter de créer plusieurs objets lors de l'ajout, il semble donc que PowerShell possède une 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

Opérateur de plage et boucle 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

Remarques:

  • J'ai annulé la variable entre chaque exécution ($a=$null).
  • Les tests ont été réalisés sur une tablette avec processeur Atom; vous verriez probablement des vitesses plus rapides sur d'autres machines. [modifier: environ deux fois plus vite sur un ordinateur de bureau]
  • Il y avait pas mal de variations lorsque j'ai essayé plusieurs essais. Recherchez les ordres de grandeur plutôt que les chiffres exacts. 
  • Les tests ont été effectués avec PowerShell 3.0 sous Windows 8. 

Remerciements

Merci à @ halr9000 pour le tableau * n, @Scott Saad et Lee Desmond pour New-Object et @EBGreen pour ArrayList.

Merci à @ n0rd de m'avoir fait penser à la performance. 

32
Celery Man
$array = 1..5 | foreach { $false }
12
Peter Seale
$array = @()
for($i=0; $i -lt 5; $i++)
{
    $array += $i
}
9
EBGreen

Voici une autre idée. Vous devez vous rappeler que c'est .NET en dessous:

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

$arr = [Object[]]::new(5)
$arr.GetType()
$arr.Length

Résultat:

IsPublic IsSerial Name                                     BaseType                                                                                               
-------- -------- ----                                     --------                                                                                               
True     True     Object[]                                 System.Array                                                                                           
5
True     True     Object[]                                 System.Array                                                                                           
5

Utiliser new() présente un avantage distinct: lorsque vous programmez dans ISE et que vous voulez créer un objet, ISE vous indiquera toutes les combinaisons de paramètres et leurs types. Vous n'avez pas cela avec New-Object, où vous devez vous rappeler les types et l'ordre des arguments. 

 ISE IntelliSense for new object

8
Adam Luniewski

La solution que j'ai trouvée consistait à utiliser l'applet de commande New-Object pour initialiser un tableau de la taille appropriée.

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

Si je ne connais pas la taille à l'avance, j'utilise un aryliste au lieu d'un tableau.

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

Ou essayez ceci une idée. Fonctionne avec Powershell 5.0+.

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