Подтвердить что ты не робот

Инициализация массива 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:\Software\PowerShell\TestArray.ps1:4 char:10
+         $array[$ <<<< i] = $FALSE
4b9b3361

Ответ 1

Еще одна альтернатива:

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

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

ПРИМЕЧАНИЕ - есть лучшие (и более эффективные) способы сделать это... см. fooobar.com/questions/98060/... ниже в качестве примера.

Ответ 2

Вот еще два способа, оба очень сжатые.

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

Ответ 3

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

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

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

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

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

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

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

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

Ответ 4

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

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

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

Резюме

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

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

  • Это быстро.
  • Вы можете использовать любое значение, а не только значение по умолчанию для этого типа.
  • Вы можете создавать повторяющиеся значения (для иллюстрации, введите это в приглашении командной строки: (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> [email protected]()
PS> Measure-Command -Expression { for ($i=0; $i -lt 100000; $i++) {$a+=$false} } | Format-List -Property "Ticks"

Мне надоело ждать, чтобы это закончилось, поэтому ctrl + c:

PS> [email protected]()
PS> Measure-Command -Expression { for ($i=0; $i -lt    100; $i++) {$a+=$false} } | Format-List -Property "Ticks"
Ticks : 147663
PS> [email protected]()
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 для массива * n, @Scott Saad и Lee Desmond для нового объекта и @EBGreen для ArrayList.

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

Ответ 5

$array = 1..5 | foreach { $false }

Ответ 6

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

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

Или попробуйте это идея. Работает с powershell 5. 0+.

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

Ответ 11

Вот еще один типичный способ:

$array = for($i = 0; $i -le 4; $i++) { $false }