16

The only way I know to create an array in powershell is

$arr = @(1, 2, 3)

However, this creating method is not convenient if I want to create an array with large initial size such as 10000.

Because I don't want to write code like this

$arr = @(0, 0, 0, 0, 0, 0, ... ,0) # 10000 0s in this line of code

Writing code like the following is not efficient.

$arr = @()
for ($i = 1; $i -le 10000; $i++) {
  $arr += 0
}

Because whenever += operator is executed, all of the elements in the old array would be copied into a newly created array.

What's the best way to create an array with large initial size in powershell?

Brian
  • 12,145
  • 20
  • 90
  • 153

4 Answers4

34

Use New-Object in this case:

PS> $arr = New-Object int[] 10000; $arr.length
10000

Or, in PSv5+, using the static new() method on the type:

PS> $arr = [int[]]::new(10000); $arr.length
10000

These commands create a strongly typed array, using base type [int] in this example. If the use case allows it, this is preferable for reasons of performance and type safety.

If you need to create an "untyped" array the same way that PowerShell does ([System.Object[]]), substitute object for int; e.g., [object[]]::new(10000); the elements of such an array will default to $null.
TessellatingHeckler's helpful answer, however, shows a much more concise alternative that even allows you initialize all elements to a specific value.


Arrays have a fixed size; if you need an array-like data structure that you can preallocate and grow dynamically, see Bluecakes' helpful [System.Collections.ArrayList]-based answer.

[System.Collections.ArrayList] is the resizable analog to [System.Object[]], and its generic equivalent - which like the [int[]] example above - allows you to use a specific type for performance and robustness (if feasible), is [System.Collections.Generic.List[<type>]], e.g.:

PS> $lst = [System.Collections.Generic.List[int]]::New(10000); $lst.Capacity
10000

Note that - as with [System.Collections.ArrayList] - specifying an initial capacity (10000, here) does not allocate the internally used array with that size right away - the capacity value is simply stored (and exposed as property .Capacity), and an internal array with that capacity (internal size that leaves room for growth) is allocated on demand, when the first element is added to the list.

[System.Collections.Generic.List[<type>]]'s .Add() method commendably does not produce output, whereas [System.Collections.ArrayList]'s does (it returns the index of the element that was just added).

# The non-generic ArrayList's .Add() produces (usually undesired) output.
PS> $al = [System.Collections.ArrayList]::new(); $al.Add('first elem')
0  # .Add() outputs the index of the newly added item

# Simplest way to suppress this output:
PS> $null = $al.Add('first elem')
# NO output.

# The generic List[T]'s .Add() does NOT produce output.
PS> $gl = [System.Collections.Generic.List[string]]::new(); $gl.Add('first elem')
# NO output from .Add()
mklement0
  • 382,024
  • 64
  • 607
  • 775
12

Array literal multiplication:

$arr = @($null) * 10000
TessellatingHeckler
  • 27,511
  • 4
  • 48
  • 87
  • 1
    Nicely done. For creating a regular PowerShell array (`[System.Object[]]`) this is by far the simplest solution, and it allows initialization with specific values too. (With really big arrays, this initialization comes at a cost, but that will probably rarely matter). To make it specific to the OP's example: `$arr = , 0 * 10000`. – mklement0 Mar 29 '17 at 15:27
  • 1
    It's a total quibble, but perhaps interesting: `@($null)` is an application of the array-subexpression operator, not an _array literal_. Even `, $null` isn't an array literal - it's an application of `,`, the array-construction operator. To quote from [PowerShell in Action, 2nd Edition](https://www.amazon.com/Windows-PowerShell-Action-Second-Payette/dp/1935182137): "Here’s how array literals are defined in PowerShell: _They’re not. There’s no array literal notation in PowerShell._ […] instead of having array literals, there’s a set of operations that create collections as needed. " – mklement0 Mar 29 '17 at 15:40
  • @mklement0 it is interesting, and I see comma parsed as 'an operator', but I wonder what meaningful distinction it makes compared to it being "a literal"? Is it something related to it being dynamic / evaluated closer to runtime, e.g. related to being able to do `$a, $b` ? `"foo"` is a string literal, presumably, but does that imply `"$foo"` is not? – TessellatingHeckler Apr 04 '17 at 05:29
  • 1
    I guess all it means is that there's no _separate, dedicated syntax_ (such as C#'s `{ …, …, … }`) for _initializing_ arrays in PS - just operators that help you construct arrays on demand. It's not related to whether the array _elements_ are literals or not. – mklement0 Apr 04 '17 at 16:46
6

In PowerShell you are correct in that += will destroy the old array and create a new array with the new items.

For working with a large collection of items i would highly recommend using the ArrayList type from .NET as this is not a fixed size array so PowerShell will not destroy it every time you add an item to it and i've found this to work better in my projects.

Using ArrayList also means that you don't need to start with 10000 items. Because PowerShell won't need to recreate your array every time, you can start with 0 and then add each item as it's needed instead of starting with 10000.

So in your script i would create an empty ArrayList like so

[System.Collections.ArrayList]$arr = @()

and then when you need to add something to it just call .Add() (You don't need to prepopulate the array with 10000 items, it will expand as you add items).

$arr.Add([int]0)

Your example using an ArrayList:

[System.Collections.ArrayList]$arr = @()
for ($i = 1; $i -le 10000; $i++) {
    $arr.Add([int]0)
}
Bluecakes
  • 2,069
  • 17
  • 23
  • Nicely done. Can I suggest you also show how to specify an initial capacity? Also, it's worth pointing out that the `.Add()` method produces output (the index at which the element was added), which may be unexpected, `$null = $arr.Add([int] 0)` is one way to suppress that output. – mklement0 Mar 29 '17 at 14:34
1

You could always do this:

$a = @(0..9999)
$a.length

This is nearly like what you are already doing, except that you don't have to write out all the values.

Walter Mitty
  • 18,205
  • 2
  • 28
  • 58
  • The `@(...)` around the range-operator expression essentially creates the array _twice_, which is undesirable; simply use `$a = 0...9999`. That said, it seems that the OP wants to initialize all array elements to `0`. – mklement0 Jan 08 '21 at 22:01