PowerShell array initialization

Solution 1:

Here's two more ways, both very concise.

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

Solution 2:

You can also rely on the default value of the constructor if you wish to create a typed array:

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

The default value of a bool is apparently false so this works in your case. Likewise if you create a typed int[] array, you'll get the default value of 0.

Another cool way that I use to initialze arrays is with the following shorthand:

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

Or if you can you want to initialize a range, I've sometimes found this useful:

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

Hope this was somewhat helpful!

Solution 3:

Yet another alternative:

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

This one works if $arr isn't defined yet.

NOTE - there are better (and more performant) ways to do this... see https://stackoverflow.com/a/234060/4570 below as an example.

Solution 4:

The original example returns an error because the array is created empty, then you try to access the nth element to assign it a value.

The are a number of creative answers here, many I didn't know before reading this post. All are fine for a small array, but as n0rd points out, there are significant differences in performance.

Here I use Measure-Command to find out how long each initialization takes. As you might guess, any approach that uses an explicit PowerShell loop is slower than those that use .Net constructors or PowerShell operators (which would be compiled in IL or native code).

Summary

  • New-Object and @(somevalue)*n are fast (around 20k ticks for 100k elements).
  • Creating an array with the range operator n..m is 10x slower (200k ticks).
  • Using an ArrayList with the Add() method is 1000x slower than the baseline (20M ticks), as is looping through an already-sized array using for() or ForEach-Object (a.k.a. foreach,%).
  • Appending with += is the worst (2M ticks for just 1000 elements).

Overall, I'd say array*n is "best" because:

  • It's fast.
  • You can use any value, not just the default for the type.
  • You can create repeating values (to illustrate, type this at the powershell prompt: (1..10)*10 -join " " or ('one',2,3)*3)
  • Terse syntax.

The only drawback:

  • Non-obvious. If you haven't seen this construct before, it's not apparent what it does.

But keep in mind that for many cases where you would want to initialize the array elements to some value, then a strongly-typed array is exactly what you need. If you're initializing everything to $false, then is the array ever going to hold anything other than $false or $true? If not, then New-Object type[] n is the "best" approach.

Testing

Create and size a default array, then assign values:

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

Creating an array of Boolean is bit little slower than and array of Object:

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

It's not obvious what this does, the documentation for New-Object just says that the second parameter is an argument list which is passed to the .Net object constructor. In the case of arrays, the parameter evidently is the desired size.

Appending with +=

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

I got tired of waiting for that to complete, so ctrl+c then:

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

Just as (6 * 3) is conceptually similar to (6 + 6 + 6), so ($somearray * 3) ought to give the same result as ($somearray + $somearray + $somearray). But with arrays, + is concatenation rather than addition.

If $array+=$element is slow, you might expect $array*$n to also be slow, but it's not:

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

Just like Java has a StringBuilder class to avoid creating multiple objects when appending, so it seems PowerShell has an 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

Range operator, and 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

Notes:

  • I nulled the variable between each run ($a=$null).
  • Testing was on a tablet with Atom processor; you would probably see faster speeds on other machines. [edit: About twice as fast on a desktop machine.]
  • There was a fair bit of variation when I tried multiple runs. Look for the orders of magnitude rather than exact numbers.
  • Testing was with PowerShell 3.0 in Windows 8.

Acknowledgements

Thanks to @halr9000 for array*n, @Scott Saad and Lee Desmond for New-Object, and @EBGreen for ArrayList.

Thanks to @n0rd for getting me to think about performance.

Solution 5:

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