skip to content

In PowerShell, what do the `@()` and `@{}` literals each create, and how do you add an item to each afterwards?

level: juniorimportance: must knowfreq 68%

answer

  1. parentheses versus braces after the sigil
  2. positional list versus keyed lookup
  3. one of them is fixed size
  4. the plus-equals is a copy, not an append
  5. [ordered] when key order matters

basics

~20 s

@() builds an array — an ordered list of values indexed by position. @{} builds a hashtable — unordered key/value pairs. Append to an array with $a += $item; add or update a hashtable entry with $h['key'] = $value.

solid answer

~40 s

`@(...)` is the array subexpression operator: `@('web01','web02')` gives a `System.Object[]` you index by position (`$a[0]`) and count with `$a.Count`; `@()` alone is an empty array. `@{...}` is a hashtable literal: `@{ Name = 'db01'; Port = 5432 }` gives a `System.Collections.Hashtable` you read as `$h['Port']` or `$h.Port`. Adding differs in kind. A PowerShell array is a fixed-size .NET array, so `$a += 'web03'` does not append in place — it allocates a new array, copies the old elements in, and rebinds the variable. A hashtable is genuinely mutable: `$h['Ssl'] = $true` adds a key, and assigning to an existing key overwrites it. If you need insertion order preserved, use `[ordered]@{ ... }`, which produces an `OrderedDictionary` instead of a hashtable.

code

powershell · 8 lines
powershell
$servers = @('web01', 'web02')
$servers += 'web03'                 # allocates a new array and copies

$config = @{ Name = 'db01'; Port = 5432 }
$config['Ssl'] = $true              # adds a key in place
$config.Port = 5433                 # overwrites an existing value

'{0} servers, {1} config keys' -f $servers.Count, $config.Count

go deeper

for a junior

Be able to write both literals from memory, index an array with $a[0], read a hashtable with $h['key'] or $h.Key, and say plainly that += on an array rebuilds it rather than appending.

for a middle

Explain that a PowerShell array is a fixed-size .NET array while a hashtable is mutable, and know [ordered]@{} and the [pscustomobject]@{} cast for producing structured output.

for a senior

Show the production habits: wrap command output in @() so zero and one results behave like many, and reach for a generic List when a collection grows inside a loop.

for a principal

Frame the choice as a contract question — code that emits objects composes with everything downstream, while code that emits hashtables or formatted text forces every consumer to re-parse it.

## Two literals, two different data structures PowerShell has one operator for making a list of values and another for making a lookup table, and they look confusingly similar — parentheses versus braces after the `@` sigil. `@( ... )` is the **array subexpression operator**. It evaluates whatever is inside and guarantees the result is an array: ```powershell $servers = @('web01', 'web02', 'web03') $servers[0] # web01 - zero-based index $servers[-1] # web03 - negative index counts from the end $servers.Count # 3 $empty = @() # an empty array ``` You do not strictly need `@()` to make an array — `$a = 1, 2, 3` works too, because the comma is the array operator. The value of `@()` is that it *forces* an array even when the expression produces zero or one object. `@(Get-ChildItem -Filter 'nothing*')` is an empty array rather than `$null`, and a single result becomes a one-element array rather than a bare object. That is the standard defence against "my `.Count` blew up because the result was a scalar". `@{ ... }` is the **hashtable literal**. Entries are `Key = Value`, separated by semicolons on one line or by newlines across several: ```powershell $config = @{ Name = 'db01' Port = 5432 } $config['Port'] # 5432 $config.Port # 5432 - property-style access works too $config.Keys # Name, Port (order not guaranteed) ``` The result is a `System.Collections.Hashtable`. Keys are unique and lookup is by key, not by position — there is no `$config[0]` unless you literally used `0` as a key. ## Adding to each This is where the two diverge, and it is the part interviewers actually probe. A hashtable is mutable in place. `$config['Ssl'] = $true` adds the key if it is absent and overwrites the value if it is present. `$config.Remove('Port')` deletes one. `$config.Add('Ssl', $true)` also works but throws if the key already exists, which is why index assignment is the common idiom. An array is **fixed size**. PowerShell arrays are ordinary .NET arrays, and .NET arrays cannot grow. So `$servers += 'web04'` is not an append: PowerShell creates a brand-new array one element longer, copies every existing element into it, adds the new one, and points `$servers` at the result. For a handful of items that is invisible. In a loop over tens of thousands of items it becomes a real performance problem, and the fix is a growable collection such as `[System.Collections.Generic.List[string]]::new()` with `.Add()`. ## Ordered hashtables Hashtables make no promise about key order — the order you see when you print one is an implementation detail, not the order you typed. When order matters (building an object whose properties should appear in a fixed sequence, or emitting configuration a human will read), cast the literal: ```powershell $ordered = [ordered]@{ Name = 'db01'; Port = 5432 } ``` That produces a `System.Collections.Specialized.OrderedDictionary`, which keeps insertion order and additionally supports index access by position. ## Where hashtables show up beyond storage A hashtable is not just a container in PowerShell; it is a language mechanism. Two common uses worth knowing: - **Building objects.** `[pscustomobject]@{ Name = 'db01'; Port = 5432 }` turns a hashtable into a real object with `Name` and `Port` properties — the standard way to emit structured output from your own code. Cast from `[ordered]` if you care about property order. - **Splatting.** If a hashtable's keys match a command's parameter names, `Get-Thing @params` passes them all as named arguments. The sigil changes from `$` to `@` at the call site. This is how long parameter lists stay readable, and it is why hashtables turn up in scripts that never needed a lookup table. ## The common confusions Writing `@{ 'a', 'b' }` is a syntax error, not an array — braces mean key/value pairs. Writing `@('a' = 1)` is equally wrong. And `$hash.Count` counts keys while `$array.Count` counts elements, so a `Count` of 2 tells you nothing about which structure you are holding; check with `$x.GetType().Name` when in doubt.

  • Why do experienced scripters wrap a command in `@(...)` when assigning its output?
    Because a command that returns nothing yields `$null` and one that returns a single object yields a bare object, not a collection. `@(...)` forces both into an array, so `.Count` and `foreach` behave the same whether the result has zero, one, or many items. Without it, code that works on multi-result days breaks on a one-result day.
  • What is splatting, and why would you use a hashtable for it?
    Splatting passes a collection of arguments to a command as a unit. Build a hashtable whose keys are parameter names and values are the arguments, then call the command with `@` instead of `$`: `Get-Thing @params`. It keeps long parameter lists readable, lets you build arguments conditionally, and avoids backtick line continuations.
  • What is the difference between a hashtable and a PSCustomObject built from one?
    A hashtable is a keyed collection — you look values up by key and it has `Keys`, `Values`, and `Count`. `[pscustomobject]@{...}` produces an object whose keys became real properties, so it formats as a table, sorts and groups by property name, and exports cleanly to CSV or JSON. Emit objects from functions; keep hashtables for lookup and splatting.

saying these in an interview costs you the question

  • Claims @() and @{} both make lists, just different syntax
  • Thinks += appends to an array in place
  • Expects hashtable keys to come back in insertion order
  • Tries to index a hashtable by position, like $h[0]
  • Says .Count tells you whether it is an array or a hashtable

context