237

I've found some interesting behaviour in PowerShell Arrays, namely, if I declare an array as:

$array = @()

And then try to add items to it using the $array.Add("item") method, I receive the following error:

Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."

However, if I append items using $array += "item", the item is accepted without a problem and the "fixed size" restriction doesn't seem to apply.

Why is this?

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
malgca
  • 2,707
  • 2
  • 16
  • 9
  • Related: [Why should I avoid using the increase assignment operator (+=) to create a collection](https://stackoverflow.com/q/60708578/1701026) – iRon Sep 02 '20 at 09:33

3 Answers3

315

When using the $array.Add()-method, you're trying to add the element into the existing array. An array is a collection of fixed size, so you will receive an error because it can't be extended.

$array += $element creates a new array with the same elements as old one + the new item, and this new larger array replaces the old one in the $array-variable

You can use the += operator to add an element to an array. When you use it, Windows PowerShell actually creates a new array with the values of the original array and the added value. For example, to add an element with a value of 200 to the array in the $a variable, type:

    $a += 200

Source: about_Arrays

+= is an expensive operation, so when you need to add many items you should try to add them in as few operations as possible, ex:

$arr = 1..3    #Array
$arr += (4..5) #Combine with another array in a single write-operation

$arr.Count
5

If that's not possible, consider using a more efficient collection like List or ArrayList (see the other answer).

Frode F.
  • 52,376
  • 9
  • 98
  • 114
  • Thanks :) Thought it may be something like this but thought it would inefficient with large arrays, so the powershell team were doing something different. – malgca Jan 31 '13 at 07:27
  • So should be use a c# list instead of a Powershell array when having to do this? – Kellen Stuart May 23 '17 at 16:23
  • 3
    It depends. If you are going to add and remove a lot of members then yes, try `List` or `ArrayList`. They will be much faster. I personally use `+=` and array 99% of the time because I usually create short throw-away scripts where the extra seconds doesn't matter. For big scripts with lots of add/remove where I want to optimize and save time I use `List` or `ArrayList`. – Frode F. May 23 '17 at 16:42
  • 3
    As arrays are always of a fixed size, does anyone know why the `Add()` method exists? – JohnLBevan Jun 29 '17 at 12:18
  • 4
    Because it's inherited from `IList`. Try `Get-Member -InputObject @()` will show this `Add Method int IList.Add(System.Object value)` – Frode F. Jun 29 '17 at 12:21
  • 2
    So they have `.Add()` because they implement IList, but it doesn't work because they are `.IsFixedSize`. So why are they fixed size?? Why does IList have an property to check for a fixed size list thing? – TessellatingHeckler Sep 27 '17 at 22:22
  • Note that Arrays implements IList via *explicit interface*. In C#, eg, `var x = new int[0]; x.Add(1);` would be type-invalid and not compiler. PowerShell 'allows' it via dynamic member access, but fails at runtime due to being invalid as .NET array types have a fixed size - ref. https://stackoverflow.com/q/38117870/2864740 – user2864740 Aug 30 '18 at 07:11
163

If you want a dynamically sized array, then you should make a list. Not only will you get the .Add() functionality, but as @frode-f explains, dynamic arrays are more memory efficient and a better practice anyway.

And it's so easy to use.

Instead of your array declaration, try this:

$outItems = New-Object System.Collections.Generic.List[System.Object]

Adding items is simple.

$outItems.Add(1)
$outItems.Add("hi")

And if you really want an array when you're done, there's a function for that too.

$outItems.ToArray()
Sebastian Kaczmarek
  • 8,120
  • 4
  • 20
  • 38
  • 1
    I have tried this. I create it using New-Object System.Collections.Generic.List[string] but then if I do .GetType, it tells me it is an array. – Preza8 Jun 12 '17 at 12:35
  • 1
    Have you tried using the `Add()` function? I can confirm, if you create a generic `List` object as stated above, you have a mutable list for which you can add and remove items using the `Add()` and `Remove()` methods respectively. – codewario Oct 03 '17 at 20:29
  • 2
    @Preza8: `(New-Object System.Collections.Generic.List[string]).GetType().Name` yields `List\`1` for me, as expected; perhaps you applied `+=` to the variable containing the list (rather than calling the `.Add()` method), in which case the variable value would indeed be converted to an array (`System.Object[]`). – mklement0 Dec 26 '17 at 03:17
  • 1
    **Shortcut: `$a = new-object collections.generic.list[object]`** – Andrew Jun 04 '18 at 14:04
  • 1
    is this the same as: `$arr = New-Object System.Collections.ArrayList`? – Ste Jun 29 '21 at 10:46
18

The most common idiom for creating an array without using the inefficient += is something like this, from the output of a loop:

$array = foreach($i in 1..10) { 
  $i
}
$array

Adding to a preexisting array:

[collections.arraylist]$array = 1..10
$array.add(11) > $null
js2010
  • 23,033
  • 6
  • 64
  • 66
  • 2
    I really like this approach. It's much more succinct without loss of intent/clarity – Esteban Nov 04 '20 at 06:49
  • 1
    @js2010 Don't quite understand this. How would you APPEND a new value to an existing array using this approach? – fmotion1 Nov 06 '21 at 21:57
  • @Jay I think that comes up less often, but an arraylist would be more efficient than copying the whole array over again with +=. – js2010 Nov 06 '21 at 22:11