Инициализация массива в PowerShell



каков наилучший способ инициализации массива в PowerShell?



например, код



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


выдает ошибку



Array assignment failed because index '0' was out of range.
At H:SoftwarePowerShellTestArray.ps1:4 char:10
+ $array[$ <<<< i] = $FALSE
741   9  

9 ответов:

еще один вариант:

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

это работает, если $arr еще не определен.

Примечание - есть лучшие (и более эффективные) способы сделать это... см.https://stackoverflow.com/a/234060/4570 ниже в качестве примера.

вот еще два способа, оба очень лаконичным.

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

вы также можете положиться на значение по умолчанию конструктор если вы хотите создать типизированный массив:

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

значение по умолчанию буль видимо ложные так это работает в вашем случае. Аналогично, если вы создаете типизированный int[] массив, вы получите значение по умолчанию 0.

еще один классный способ, который я использую для инициализации массивов, - это следующая стенография:

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

или если вы хотите чтобы инициализировать диапазон, я иногда находил это полезным:

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

надеюсь, это было полезно!

исходный пример возвращает ошибку, поскольку массив создается пустым, а затем вы пытаетесь получить доступ к N-му элементу, чтобы присвоить ему значение.

здесь есть несколько творческих ответов, многие из которых я не знал, прежде чем читать этот пост. Все они хороши для небольшого массива, но, как указывает n0rd, существуют значительные различия в производительности.

здесь я использую команду Measure-Command, чтобы узнать, сколько времени занимает каждая инициализация. Как вы можете догадаться, любой подход, который использует явный цикл PowerShell выполняется медленнее, чем те, которые используют .NET-конструкторы или операторы PowerShell (которые будут скомпилированы в IL или собственном коде).

резюме

  • New-Object и @(somevalue)*n быстро (около 20k тиков для 100k элементов).
  • Создание массива с оператором диапазона n..m в 10 раз медленнее (200k тиков).
  • используя ArrayList с Add() метод в 1000 раз медленнее, чем базовая линия (20 м тиков), как и цикл через массив уже определенного размера с помощью for() или ForEach-Object (а.к.а. foreach,%).
  • С добавлением += является худшим (2M тиков для всего 1000 элементов).

в целом, я бы сказал массив*n "лучший", потому что:

  • это быстро.
  • вы можете использовать любое значение, а не только по умолчанию для типа.
  • можно создать повторяющиеся значения (для иллюстрации введите это в командной строке PowerShell: (1..10)*10 -join " " или ('one',2,3)*3)
  • лаконичный синтаксис.

единственный недостаток:

  • неочевидным. Если вы не видели эту конструкцию раньше, то не ясно, что она делает.

но имейте в виду, что для многих случаев, когда вы хотите инициализировать элементы массива какое-то значение, то строго типизированный массив-это именно то, что нужно. Если вы инициализируете все в $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

Создание массива Boolean немного медленнее, чем и массив объекта:

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; вы вероятно, вы увидите более высокие скорости на других машинах. [edit: примерно в два раза быстрее на настольной машине.]
  • было довольно много вариаций, когда я попробовал несколько запусков. Ищите порядки величины, а не точные цифры.
  • тестирование было с PowerShell 3.0 В Windows 8.

благодарности

спасибо @halr9000 для array*n, @Scott Saad и Lee Desmond для New-Object, и @EBGreen для список ArrayList.

спасибо @n0rd за то, что заставил меня думать о производительности.

$array = 1..5 | foreach { $false }
$array = @()
for($i=0; $i -lt 5; $i++)
{
    $array += $i
}

решение, которое я нашел, состояло в том, чтобы использовать командлет New-Object для инициализации массива нужного размера.

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

вот еще одна идея. Вы должны помнить, что это .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 for new object

Если я не знаю размер спереди, я использую arraylist вместо массива.

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

Comments

    Ничего не найдено.