Исходный пример возвращает ошибку, поскольку массив создается пустым, затем вы пытаетесь получить доступ к n-му элементу, чтобы присвоить ему значение.
Здесь есть несколько креативных ответов, многие из которых я не знал до прочтения этого поста. Все хорошо для небольшого массива, но, как указывает n0rd, существуют значительные различия в производительности.
Здесь я использую Measure-Command, чтобы узнать, сколько времени занимает каждая инициализация. Как вы можете догадаться, любой подход, использующий явный цикл PowerShell, работает медленнее, чем тот, который использует конструкторы .Net или операторы PowerShell (которые будут скомпилированы в IL или в собственном коде).
Резюме
New-Object
и @(somevalue)*n
быстрые (около 20 тыс. Тиков на 100 тыс. Элементов).
- Создание массива с оператором диапазона
n..m
медленнее в 10 раз (200 тыс. Тиков).
- Использование ArrayList с методом
Add()
в 1000 раз медленнее, чем базовая линия (20M тиков), как и при циклическом просмотре массива уже размера с использованием 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 немного медленнее, чем массив Object:
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
loop:
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; Вы, вероятно, увидите более высокие скорости на других машинах. [править: примерно вдвое быстрее на настольном компьютере.]
- Было несколько вариаций, когда я пробовал несколько прогонов. Ищите порядки, а не точные числа.
- Тестирование проводилось с помощью PowerShell 3.0 в Windows 8.
Подтверждения
Спасибо @ halr9000 за массив * n, @ Скотту Сааду и Ли Десмонду за New-Object и @EBGreen за ArrayList.
Спасибо @ n0rd за то, что заставили меня задуматься о производительности.