Инициализация массива в 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
9 ответов:
еще один вариант:
for ($i = 0; $i -lt 5; $i++) { $arr += @($false) }это работает, если $arr еще не определен.
Примечание - есть лучшие (и более эффективные) способы сделать это... см.https://stackoverflow.com/a/234060/4570 ниже в качестве примера.
вы также можете положиться на значение по умолчанию конструктор если вы хотите создать типизированный массив:
> $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 за то, что заставил меня думать о производительности.
решение, которое я нашел, состояло в том, чтобы использовать командлет 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, где вы должны помнить, типы и порядок аргументов.
Если я не знаю размер спереди, я использую arraylist вместо массива.
$al = New-Object System.Collections.ArrayList for($i=0; $i -lt 5; $i++) { $al.Add($i) }

Comments