Skip to main content

PowerShell Arrays: Complete Guide to Creating and Managing Arrays [2026]

12 min read
powershell arrays data-structures collections powershell-basics

PowerShell arrays are fundamental data structures that provide a structured way to store and manipulate collections of items. Whether you’re managing server lists, processing log files, or automating AD tasks, understanding arrays is essential for efficient PowerShell scripting.

In this comprehensive guide, you’ll learn everything about PowerShell arrays—from basic creation to advanced manipulation techniques. We’ll cover string arrays, nested arrays, array properties, methods, and real-world use cases with practical examples.

Table of Contents

What is a PowerShell Array?

A PowerShell array is an ordered collection of objects stored in a single variable. Arrays can hold multiple values of the same type (homogeneous) or different types (heterogeneous), making them incredibly flexible for data management.

Key characteristics of PowerShell arrays:

  • Zero-indexed: First element is at position [0]
  • Fixed-size by default: Once created, the size is fixed (though you can create new arrays)
  • Type-flexible: Can store strings, integers, objects, or mixed types
  • Reference type: Arrays are objects, not primitive types
  • Immutable size: Adding/removing elements creates a new array
# PowerShell array example
$servers = @("DC01", "DC02", "FS01", "SQL01")
Write-Host "Server count: $($servers.Count)"
Write-Host "First server: $($servers[0])"

Output:

Server count: 4
First server: DC01

Why Use Arrays in PowerShell?

Arrays are essential for PowerShell automation and scripting. Here’s why:

1. Manage Multiple Items Efficiently Instead of creating separate variables for each server, user, or file, store them in a single array:

# Without arrays (inefficient)
$server1 = "DC01"
$server2 = "DC02"
$server3 = "DC03"

# With arrays (efficient)
$servers = @("DC01", "DC02", "DC03")

2. Enable Batch Processing Process multiple items in loops for automation:

# Check disk space on multiple servers
$servers = @("DC01", "DC02", "FS01")
foreach ($server in $servers) {
    Get-WmiObject Win32_LogicalDisk -ComputerName $server -Filter "DeviceID='C:'" |
        Select-Object @{Name="Server";Expression={$server}}, FreeSpace, Size
}

3. Store Command Results Capture output from cmdlets for further processing:

# Get all stopped services
$stoppedServices = Get-Service | Where-Object {$_.Status -eq 'Stopped'}
Write-Host "Found $($stoppedServices.Count) stopped services"

4. Pass Data Between Functions Arrays make it easy to pass multiple values to functions:

function Test-ServerConnection {
    param([string[]]$ServerList)

    foreach ($server in $ServerList) {
        Test-Connection -ComputerName $server -Count 1 -Quiet
    }
}

$servers = @("DC01", "DC02", "FS01")
Test-ServerConnection -ServerList $servers

5. Work with CSV and JSON Data Import data from files as arrays for processing:

# Import CSV as array of objects
$users = Import-Csv -Path "C:\Users\users.csv"
foreach ($user in $users) {
    New-ADUser -Name $user.Name -SamAccountName $user.SamAccountName
}

For more advanced PowerShell concepts, see our Complete PowerShell Guide.

How to Create Array in PowerShell {#creating-arrays}

PowerShell offers multiple ways to create arrays. Here are all the methods you need to know:

Method 1: Array Subexpression Operator @()

The @() operator explicitly creates an array, even with zero or one element:

# Create an empty array
$empty = @()

# Create an array with one element
$single = @("Apple")

# Create an array with multiple elements
$fruits = @("Apple", "Banana", "Orange", "Grape")

# Verify it's an array
$fruits.GetType().Name  # Output: Object[]

Output:

Object[]

Method 2: Comma-Separated Values

PowerShell automatically creates an array when you use commas:

# Simple array creation
$numbers = 1, 2, 3, 4, 5

# Mixed type array
$mixed = "Server01", 42, $true, (Get-Date)

# Check the count
$numbers.Count  # Output: 5

Output:

5

Method 3: Range Operator (..)

Create sequential number arrays quickly:

# Create array of numbers 1-10
$range = 1..10

# Create array from 0 to 100
$hundred = 0..100

# Reverse range
$reverse = 10..1

# Display range
$range

Output:

1
2
3
4
5
6
7
8
9
10

Method 4: From Cmdlet Output

Capture cmdlet results directly into arrays:

# Get all .txt files in a directory
$textFiles = Get-ChildItem -Path "C:\Logs" -Filter "*.txt"

# Get all running processes
$runningProcesses = Get-Process

# Get AD users from specific OU
$users = Get-ADUser -Filter * -SearchBase "OU=Users,DC=contoso,DC=com"

# Display count
Write-Host "Found $($textFiles.Count) text files"

Method 5: Strongly Typed Arrays

Create arrays that only accept specific types:

# String array only
[string[]]$names = "John", "Jane", "Bob"

# Integer array only
[int[]]$ages = 25, 30, 35

# Try to add wrong type (this will error)
# [int[]]$numbers = 1, 2, "three"  # Error: Cannot convert "three" to int

# DateTime array
[datetime[]]$dates = (Get-Date), (Get-Date).AddDays(1)

Method 6: ArrayList (Dynamic Size)

For frequently modified arrays, use ArrayList:

# Create ArrayList
$list = [System.Collections.ArrayList]@()

# Add items (more efficient than array +=)
$list.Add("Item1") | Out-Null
$list.Add("Item2") | Out-Null
$list.Add("Item3") | Out-Null

# Display
$list

Output:

Item1
Item2
Item3

💡 Pro Tip: Use @() when you need to ensure the result is always an array, even if the command returns zero or one item. This prevents errors when using array methods.

How to Access Array Elements {#accessing-array-elements}

PowerShell provides flexible indexing to access array elements. Arrays are zero-indexed, meaning the first element is at position [0].

Basic Index Access

$fruits = @("Apple", "Banana", "Orange", "Grape", "Mango")

# Access first element
$fruits[0]  # Output: Apple

# Access third element
$fruits[2]  # Output: Orange

# Access last element
$fruits[4]  # Output: Mango

Output:

Apple
Orange
Mango

Negative Indexing

Access elements from the end of the array using negative indices:

$fruits = @("Apple", "Banana", "Orange", "Grape", "Mango")

# Last element
$fruits[-1]  # Output: Mango

# Second to last
$fruits[-2]  # Output: Grape

# Third from end
$fruits[-3]  # Output: Orange

Output:

Mango
Grape
Orange

Range Access (Slicing)

Extract multiple elements using ranges:

$numbers = 1..10

# Get first three elements (index 0, 1, 2)
$numbers[0..2]  # Output: 1, 2, 3

# Get elements 5-8
$numbers[4..7]  # Output: 5, 6, 7, 8

# Get last three elements
$numbers[-3..-1]  # Output: 8, 9, 10

Output:

1
2
3

Multiple Index Access

Access non-consecutive elements:

$servers = @("DC01", "DC02", "FS01", "SQL01", "WEB01", "APP01")

# Get first, third, and fifth elements
$servers[0,2,4]  # Output: DC01, FS01, WEB01

# Get elements using variable indices
$indices = 1, 3, 5
$servers[$indices]  # Output: DC02, SQL01, APP01

Output:

DC01
FS01
WEB01

Accessing Nested Array Elements

For multi-dimensional arrays:

# Create nested array
$matrix = @(
    @(1, 2, 3),
    @(4, 5, 6),
    @(7, 8, 9)
)

# Access row 2, column 3
$matrix[1][2]  # Output: 6

# Access first row
$matrix[0]  # Output: 1, 2, 3

Output:

6

Safe Access (Avoiding Index Errors)

Prevent errors when accessing potentially non-existent indices:

$fruits = @("Apple", "Banana", "Orange")

# This will cause an error if index doesn't exist
# $fruits[10]  # Error: Index was out of range

# Safe access with bounds checking
if ($fruits.Count -gt 10) {
    $fruits[10]
} else {
    Write-Host "Index out of bounds"
}

# Using try-catch
try {
    $value = $fruits[10]
} catch {
    Write-Host "Cannot access index 10"
}

⚠️ Warning: Accessing an index beyond the array size will return $null or throw an error depending on the context. Always check $array.Count before accessing by index.

How to Add Elements to Array {#adding-elements}

Adding elements to PowerShell arrays requires understanding that standard arrays are fixed-size. The += operator creates a new array with the additional element.

Method 1: Using += Operator

The simplest way to add elements:

$fruits = @("Apple", "Banana")

# Add single element
$fruits += "Orange"

# Add multiple elements
$fruits += "Grape", "Mango"

# Display result
$fruits

Output:

Apple
Banana
Orange
Grape
Mango

Method 2: Array Concatenation

Combine two arrays:

$array1 = @("Item1", "Item2")
$array2 = @("Item3", "Item4")

# Concatenate arrays
$combined = $array1 + $array2

# Display result
$combined

Output:

Item1
Item2
Item3
Item4

Method 3: Using ArrayList (Better Performance)

For frequent additions, use ArrayList instead:

# Convert array to ArrayList
$list = [System.Collections.ArrayList]@("Apple", "Banana")

# Add elements (much faster than +=)
$list.Add("Orange") | Out-Null
$list.Add("Grape") | Out-Null

# Or add multiple at once
$list.AddRange(@("Mango", "Peach"))

# Display
$list

Output:

Apple
Banana
Orange
Grape
Mango
Peach

Method 4: Using Generic List (Best Performance)

For maximum performance with type safety:

# Create generic list of strings
$list = [System.Collections.Generic.List[string]]::new()

# Add elements
$list.Add("Apple")
$list.Add("Banana")
$list.Add("Orange")

# Add multiple
$list.AddRange(@("Grape", "Mango"))

# Display
$list

Method 5: Insert at Specific Position

Insert elements at specific index using ArrayList:

$list = [System.Collections.ArrayList]@("First", "Third", "Fourth")

# Insert at index 1
$list.Insert(1, "Second")

# Display
$list

Output:

First
Second
Third
Fourth

Performance Comparison

# Measure performance of += operator (slow)
Measure-Command {
    $array = @()
    for ($i = 0; $i -lt 1000; $i++) {
        $array += $i
    }
}
# Time: ~450ms

# Measure ArrayList performance (fast)
Measure-Command {
    $list = [System.Collections.ArrayList]@()
    for ($i = 0; $i -lt 1000; $i++) {
        $list.Add($i) | Out-Null
    }
}
# Time: ~15ms

💡 Pro Tip: If you need to add elements frequently (more than 5-10 times), use ArrayList or List<T> instead of the += operator. The performance difference is significant for large datasets.

⚠️ Warning: Each += operation creates a new array and copies all elements, making it O(n²) complexity for multiple additions. For loops with many additions, this becomes very slow.

How to Remove Elements from Array {#removing-elements}

Since PowerShell arrays are fixed-size, removing elements requires creating a new array without the unwanted elements.

Method 1: Using Where-Object

Filter out specific values:

$fruits = @("Apple", "Banana", "Orange", "Banana", "Grape")

# Remove all "Banana" elements
$fruits = $fruits | Where-Object { $_ -ne "Banana" }

# Display result
$fruits

Output:

Apple
Orange
Grape

Method 2: Remove by Index

Remove element at specific position:

$numbers = 1, 2, 3, 4, 5

# Remove element at index 2 (value 3)
$numbers = $numbers[0..1] + $numbers[3..4]

# Or using Where-Object with index
$numbers = $numbers | Where-Object { $numbers.IndexOf($_) -ne 2 }

# Display
$numbers

Output:

1
2
4
5

Method 3: Remove Using ArrayList

More efficient for multiple removals:

# Create ArrayList
$list = [System.Collections.ArrayList]@("Apple", "Banana", "Orange", "Grape")

# Remove specific item
$list.Remove("Banana")

# Remove at specific index
$list.RemoveAt(0)  # Removes "Apple"

# Remove range
$list.RemoveRange(0, 2)  # Removes first 2 elements

# Display
$list

Method 4: Remove Duplicates

Keep only unique values:

$numbers = 1, 2, 2, 3, 3, 3, 4, 5, 5

# Remove duplicates using Select-Object
$unique = $numbers | Select-Object -Unique

# Or using Get-Unique (requires sorted array)
$unique = $numbers | Sort-Object | Get-Unique

# Display
$unique

Output:

1
2
3
4
5

Method 5: Remove Null or Empty Values

Clean up arrays with empty elements:

$mixed = @("Apple", "", "Banana", $null, "Orange", " ", "Grape")

# Remove null and empty strings
$cleaned = $mixed | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }

# Display
$cleaned

Output:

Apple
Banana
Orange
Grape

Method 6: Remove Elements Matching Pattern

Use regex or wildcard patterns:

$files = @("report.txt", "data.csv", "summary.txt", "output.log", "notes.txt")

# Remove all .txt files
$filtered = $files | Where-Object { $_ -notlike "*.txt" }

# Or using regex
$filtered = $files | Where-Object { $_ -notmatch '\.txt$' }

# Display
$filtered

Output:

data.csv
output.log

Method 7: Bulk Remove Using -notin

Remove multiple values at once:

$servers = @("DC01", "DC02", "FS01", "SQL01", "WEB01")
$decommissioned = @("FS01", "SQL01")

# Remove decommissioned servers
$active = $servers | Where-Object { $_ -notin $decommissioned }

# Display
$active

Output:

DC01
DC02
WEB01

💡 Pro Tip: For performance-critical code with frequent removals, use List<T>.RemoveAll() method instead of filtering arrays repeatedly.

Array Properties and Length {#array-properties}

PowerShell arrays have several useful properties for getting information about the array.

Count vs Length Property

Both properties return the number of elements:

$fruits = @("Apple", "Banana", "Orange", "Grape", "Mango")

# Using Count property
$fruits.Count  # Output: 5

# Using Length property
$fruits.Length  # Output: 5

# Both are equivalent for arrays
$fruits.Count -eq $fruits.Length  # Output: True

Output:

5
5
True

Checking for Empty Arrays

$empty = @()
$notEmpty = @("Item")

# Check if array is empty
if ($empty.Count -eq 0) {
    Write-Host "Array is empty"
}

# Or use Length
if ($notEmpty.Length -gt 0) {
    Write-Host "Array has $($notEmpty.Length) elements"
}

Output:

Array is empty
Array has 1 elements

Other Useful Properties

$numbers = 1, 2, 3, 4, 5

# Get array rank (dimensions)
$numbers.Rank  # Output: 1 (one-dimensional)

# Check if array is fixed size
$numbers.IsFixedSize  # Output: True

# Check if array is read-only
$numbers.IsReadOnly  # Output: False

# Get array type
$numbers.GetType().Name  # Output: Object[]

LongLength for Large Arrays

For arrays with more than 2 billion elements (theoretical):

$largeArray = 1..1000000

# Use LongLength for very large arrays
$largeArray.LongLength  # Returns as Int64 (long)

Bounds Information

Get the upper and lower bounds:

$fruits = @("Apple", "Banana", "Orange")

# Get lower bound (always 0 for PowerShell arrays)
$fruits.GetLowerBound(0)  # Output: 0

# Get upper bound (last index)
$fruits.GetUpperBound(0)  # Output: 2

# Calculate length from bounds
$length = $fruits.GetUpperBound(0) - $fruits.GetLowerBound(0) + 1
Write-Host "Array length: $length"

Output:

0
2
Array length: 3

💡 Pro Tip: Use .Count instead of .Length for consistency across all PowerShell collections (some collection types only have .Count).

Looping Through Arrays {#looping-through-arrays}

PowerShell provides multiple ways to iterate through array elements. Each method has specific use cases.

Method 1: foreach Loop (Fastest)

The most common and fastest method:

$servers = @("DC01", "DC02", "FS01", "SQL01")

foreach ($server in $servers) {
    Write-Host "Processing: $server"
    # Test connection
    Test-Connection -ComputerName $server -Count 1 -Quiet
}

Output:

Processing: DC01
Processing: DC02
Processing: FS01
Processing: SQL01

Method 2: ForEach-Object Cmdlet (Pipeline)

Use when working with pipeline data:

$numbers = 1..5

# Using ForEach-Object
$numbers | ForEach-Object {
    $_ * 2
}

# With named parameter
$numbers | ForEach-Object -Process { $_ * 2 }

Output:

2
4
6
8
10

Method 3: for Loop with Index

When you need the index position:

$fruits = @("Apple", "Banana", "Orange", "Grape")

for ($i = 0; $i -lt $fruits.Count; $i++) {
    Write-Host "[$i] = $($fruits[$i])"
}

Output:

[0] = Apple
[1] = Banana
[2] = Orange
[3] = Grape

Method 4: while Loop

Less common but useful for conditional iteration:

$numbers = 1..5
$index = 0

while ($index -lt $numbers.Count) {
    Write-Host "Number: $($numbers[$index])"
    $index++
}

Method 5: .ForEach() Method

PowerShell 4.0+ intrinsic method:

$numbers = 1..5

# Execute script block for each element
$numbers.ForEach({ $_ * 2 })

# With multiple statements
$servers = @("DC01", "DC02", "FS01")
$servers.ForEach({
    Write-Host "Server: $_"
    Test-Connection -ComputerName $_ -Count 1 -Quiet
})

Method 6: Reverse Iteration

Loop through array backwards:

$fruits = @("Apple", "Banana", "Orange", "Grape")

# Method 1: Reverse for loop
for ($i = $fruits.Count - 1; $i -ge 0; $i--) {
    Write-Host $fruits[$i]
}

# Method 2: Reverse the array first
$fruits[($fruits.Count-1)..0] | ForEach-Object {
    Write-Host $_
}

Output:

Grape
Orange
Banana
Apple

Method 7: Nested Loops (Multi-dimensional Arrays)

Iterate through nested arrays:

$matrix = @(
    @(1, 2, 3),
    @(4, 5, 6),
    @(7, 8, 9)
)

foreach ($row in $matrix) {
    foreach ($value in $row) {
        Write-Host $value -NoNewline
        Write-Host " " -NoNewline
    }
    Write-Host ""  # New line
}

Output:

1 2 3
4 5 6
7 8 9

Performance Comparison

$largeArray = 1..10000

# foreach loop (fastest)
Measure-Command {
    foreach ($item in $largeArray) { $item * 2 }
}
# ~15ms

# ForEach-Object (slower due to pipeline overhead)
Measure-Command {
    $largeArray | ForEach-Object { $_ * 2 }
}
# ~180ms

# .ForEach() method (fast)
Measure-Command {
    $largeArray.ForEach({ $_ * 2 })
}
# ~25ms

💡 Pro Tip: Use foreach loop for maximum performance. Use ForEach-Object when working with pipelines or when you need advanced features like -Parallel in PowerShell 7+.

Array Methods {#array-methods}

PowerShell arrays inherit methods from the .NET System.Array class, providing powerful manipulation capabilities.

Sort Arrays

$numbers = 5, 2, 8, 1, 9, 3

# Sort ascending
[Array]::Sort($numbers)
$numbers  # Output: 1, 2, 3, 5, 8, 9

# Sort descending
[Array]::Reverse($numbers)
$numbers  # Output: 9, 8, 5, 3, 2, 1

Reverse Arrays

$fruits = @("Apple", "Banana", "Orange")

# Reverse the array
[Array]::Reverse($fruits)
$fruits  # Output: Orange, Banana, Apple

Find Elements

$servers = @("DC01", "DC02", "FS01", "SQL01", "WEB01")

# Find index of element
$index = [Array]::IndexOf($servers, "SQL01")
Write-Host "SQL01 is at index: $index"  # Output: 3

# Find last index
$lastIndex = [Array]::LastIndexOf($servers, "DC01")

# Check if element exists
$exists = [Array]::Exists($servers, [Predicate[string]]{ $args[0] -eq "FS01" })
Write-Host "FS01 exists: $exists"  # Output: True

Clear and Copy

$original = 1, 2, 3, 4, 5
$destination = New-Object int[] 5

# Copy array
[Array]::Copy($original, $destination, 5)
$destination  # Output: 1, 2, 3, 4, 5

# Clear array elements
[Array]::Clear($original, 0, $original.Length)
$original  # Output: 0, 0, 0, 0, 0 (for int arrays)

Filter and Find

$numbers = 1..20

# Find all even numbers
$even = [Array]::FindAll($numbers, [Predicate[int]]{ $args[0] % 2 -eq 0 })
$even  # Output: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20

# Find first element matching condition
$firstEven = [Array]::Find($numbers, [Predicate[int]]{ $args[0] % 2 -eq 0 })
$firstEven  # Output: 2

Convert and Join

$array = @("PowerShell", "is", "awesome")

# Join array elements into string
$joined = $array -join " "
Write-Host $joined  # Output: PowerShell is awesome

# Join with custom separator
$csv = $array -join ", "
Write-Host $csv  # Output: PowerShell, is, awesome

# Convert array to different type
$strings = @("1", "2", "3")
$integers = [int[]]$strings
$integers  # Output: 1, 2, 3 (as integers)

String Arrays {#string-arrays}

String arrays are one of the most common array types in PowerShell scripting.

Creating String Arrays

# Explicit string array
[string[]]$names = "John", "Jane", "Bob", "Alice"

# String array from split operation
$csv = "Server1,Server2,Server3,Server4"
$servers = $csv -split ","

# String array from file
$lines = Get-Content -Path "C:\servers.txt"

String Array Operations

$servers = @("DC01", "DC02", "FS01", "SQL01")

# Filter strings containing pattern
$filtered = $servers -like "DC*"
$filtered  # Output: DC01, DC02

# Case-insensitive matching
$matches = $servers -match "dc01"
$matches  # Output: DC01

# Replace in all strings
$updated = $servers -replace "01", "02"
$updated  # Output: DC02, DC02, FS02, SQL02

String Array Comparison

$array1 = @("Apple", "Banana", "Orange")
$array2 = @("Banana", "Grape", "Mango")

# Find common elements
$common = $array1 | Where-Object { $array2 -contains $_ }
$common  # Output: Banana

# Find differences
$unique1 = $array1 | Where-Object { $array2 -notcontains $_ }
$unique1  # Output: Apple, Orange

Converting String Arrays

# Convert to uppercase
$names = @("john", "jane", "bob")
$upper = $names.ForEach({ $_.ToUpper() })
$upper  # Output: JOHN, JANE, BOB

# Convert to lowercase
$lower = $names.ForEach({ $_.ToLower() })

# Trim whitespace
$messy = @("  Apple  ", " Banana", "Orange  ")
$clean = $messy.ForEach({ $_.Trim() })
$clean  # Output: Apple, Banana, Orange

For more string manipulation techniques, see our PowerShell Strings guide.

Nested Arrays (Multi-dimensional) {#nested-arrays}

Nested arrays (arrays of arrays) allow you to create matrix-like structures.

Creating Nested Arrays

# 2D array (matrix)
$matrix = @(
    @(1, 2, 3),
    @(4, 5, 6),
    @(7, 8, 9)
)

# Access elements
$matrix[0][0]  # Output: 1
$matrix[1][2]  # Output: 6
$matrix[2][1]  # Output: 8

# Get entire row
$matrix[0]  # Output: 1, 2, 3

Jagged Arrays

Arrays with varying row lengths:

$jagged = @(
    @(1, 2),
    @(3, 4, 5),
    @(6, 7, 8, 9)
)

# Access elements
$jagged[1][2]  # Output: 5
$jagged[2].Count  # Output: 4

Creating Multi-dimensional Arrays

# Create 3x3 multi-dimensional array
$multiArray = New-Object 'int[,]' 3, 3

# Populate values
for ($i = 0; $i -lt 3; $i++) {
    for ($j = 0; $j -lt 3; $j++) {
        $multiArray[$i, $j] = ($i * 3) + $j + 1
    }
}

# Access elements
$multiArray[1, 1]  # Output: 5

Practical Example: Server Inventory

# Server inventory as nested array
$inventory = @(
    @{Name="DC01"; IP="192.168.1.10"; Role="Domain Controller"},
    @{Name="FS01"; IP="192.168.1.20"; Role="File Server"},
    @{Name="SQL01"; IP="192.168.1.30"; Role="Database Server"}
)

# Access server details
foreach ($server in $inventory) {
    Write-Host "$($server.Name) - $($server.IP) - $($server.Role)"
}

Output:

DC01 - 192.168.1.10 - Domain Controller
FS01 - 192.168.1.20 - File Server
SQL01 - 192.168.1.30 - Database Server

Array vs ArrayList vs Generic List {#array-comparison}

Understanding the differences helps you choose the right collection type.

Comparison Table

FeatureArrayArrayListList
SizeFixedDynamicDynamic
Performance (Add)O(n)O(1)*O(1)*
Type SafetyOptionalNoYes
Syntax@()[ArrayList]@()[List[string]]::new()
MemoryEfficientMore overheadMost efficient
Use CaseStatic dataLegacy codeModern scripts

When to Use Each

Use Array when:

  • Size is known and won’t change
  • Working with small datasets (<100 items)
  • Simple read-only operations
  • Interfacing with cmdlets expecting arrays
# Good use of array
$daysOfWeek = @("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")

Use ArrayList when:

  • Size changes frequently
  • Working with legacy code
  • Need dynamic collection without type safety
# ArrayList for dynamic collection
$log = [System.Collections.ArrayList]@()
foreach ($file in Get-ChildItem) {
    $log.Add($file.Name) | Out-Null
}

Use List when:

  • Need type safety
  • Frequent additions/removals
  • Modern PowerShell scripts (5.1+)
  • Performance is critical
# Generic List for best performance
$servers = [System.Collections.Generic.List[string]]::new()
$servers.Add("DC01")
$servers.Add("DC02")

Performance Benchmark

# Array += (slowest)
$array = @()
Measure-Command {
    1..1000 | ForEach-Object { $array += $_ }
}
# ~450ms

# ArrayList (fast)
$list = [ArrayList]@()
Measure-Command {
    1..1000 | ForEach-Object { $list.Add($_) | Out-Null }
}
# ~15ms

# Generic List (fastest)
$genericList = [System.Collections.Generic.List[int]]::new()
Measure-Command {
    1..1000 | ForEach-Object { $genericList.Add($_) }
}
# ~12ms

Real-World Use Cases {#real-world-use-cases}

Here are practical scenarios where arrays are essential.

Use Case 1: Bulk Server Management

# List of servers to manage
$servers = @("DC01", "DC02", "FS01", "SQL01", "WEB01")

# Check disk space on all servers
foreach ($server in $servers) {
    $disk = Get-WmiObject Win32_LogicalDisk -ComputerName $server -Filter "DeviceID='C:'"
    $freeGB = [math]::Round($disk.FreeSpace / 1GB, 2)
    $totalGB = [math]::Round($disk.Size / 1GB, 2)
    $percentFree = [math]::Round(($freeGB / $totalGB) * 100, 2)

    Write-Host "$server - Free: ${freeGB}GB / ${totalGB}GB ($percentFree%)"
}

Use Case 2: User Provisioning from CSV

# Import users from CSV
$users = Import-Csv -Path "C:\NewUsers.csv"

# Create AD accounts
foreach ($user in $users) {
    try {
        New-ADUser -Name $user.Name `
                   -SamAccountName $user.SamAccountName `
                   -EmailAddress $user.Email `
                   -Department $user.Department `
                   -Enabled $true
        Write-Host "Created: $($user.Name)" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed: $($user.Name) - $_" -ForegroundColor Red
    }
}

For more on Active Directory management, see our Complete Active Directory Guide.

Use Case 3: Log File Analysis

# Read log file into array
$logLines = Get-Content -Path "C:\Logs\app.log"

# Filter error lines
$errors = $logLines | Where-Object { $_ -match "ERROR" }

# Group by error type
$errorSummary = $errors | ForEach-Object {
    if ($_ -match "ERROR:\s*(.+)") {
        $matches[1]
    }
} | Group-Object | Sort-Object Count -Descending

# Display summary
$errorSummary | Select-Object Count, Name

Use Case 4: File Batch Operations

# Get all .txt files
$textFiles = Get-ChildItem -Path "C:\Documents" -Filter "*.txt"

# Rename with date prefix
$textFiles | ForEach-Object {
    $newName = "{0:yyyy-MM-dd}_{1}" -f (Get-Date), $_.Name
    Rename-Item -Path $_.FullName -NewName $newName
    Write-Host "Renamed: $($_.Name) -> $newName"
}

Use Case 5: Email Distribution List

# Email recipients array
$recipients = @(
    "admin@contoso.com",
    "manager@contoso.com",
    "support@contoso.com"
)

# Send report to all recipients
$reportPath = "C:\Reports\DailyReport.pdf"

foreach ($recipient in $recipients) {
    Send-MailMessage -To $recipient `
                     -From "reports@contoso.com" `
                     -Subject "Daily Report - $(Get-Date -Format 'yyyy-MM-dd')" `
                     -Body "Please find attached today's report." `
                     -Attachments $reportPath `
                     -SmtpServer "smtp.contoso.com"

    Write-Host "Sent report to $recipient"
}

Troubleshooting Common Array Issues {#troubleshooting}

Issue 1: “Index was outside the bounds of the array”

Cause: Accessing an index that doesn’t exist

$array = @("One", "Two", "Three")
$array[5]  # Error!

Solution: Check array bounds before accessing

$index = 5
if ($index -lt $array.Count) {
    $array[$index]
} else {
    Write-Host "Index $index is out of bounds (array size: $($array.Count))"
}

Issue 2: += Operator Performance

Cause: Each += operation creates a new array

Solution: Use ArrayList or List

# Slow
$array = @()
1..1000 | ForEach-Object { $array += $_ }  # ~450ms

# Fast
$list = [System.Collections.Generic.List[int]]::new()
1..1000 | ForEach-Object { $list.Add($_) }  # ~12ms

Issue 3: Null Array Elements

Cause: Uninitialized array elements or failed operations

$array = @(1, 2, $null, 4, 5)

# This might cause issues
$array | ForEach-Object { $_ * 2 }  # Error on $null

Solution: Filter null values

$array | Where-Object { $null -ne $_ } | ForEach-Object { $_ * 2 }

Issue 4: Array vs Single Object

Cause: Cmdlets may return single object instead of array

$files = Get-ChildItem -Path "C:\SingleFile"
$files.Count  # Returns $null if only one file!

Solution: Force array with @()

$files = @(Get-ChildItem -Path "C:\SingleFile")
$files.Count  # Always returns valid count

Issue 5: Modifying Array During Iteration

Cause: Changing array size while looping

# This will fail
$array = 1, 2, 3, 4, 5
foreach ($item in $array) {
    $array += 100  # Don't modify during iteration!
}

Solution: Create new array or use copy

$original = 1, 2, 3, 4, 5
$modified = $original.Clone()

foreach ($item in $original) {
    $modified += 100
}

Common Mistakes to Avoid {#common-mistakes}

Mistake 1: Forgetting Arrays Are Zero-Indexed

# Wrong: Assuming first element is [1]
$servers = @("DC01", "DC02", "FS01")
$first = $servers[1]  # This is DC02, not DC01!

# Correct: First element is [0]
$first = $servers[0]  # DC01

Mistake 2: Using += in Loops

# Inefficient
$results = @()
foreach ($i in 1..1000) {
    $results += $i  # Creates 1000 new arrays!
}

# Efficient
$results = [System.Collections.Generic.List[int]]::new()
foreach ($i in 1..1000) {
    $results.Add($i)  # O(1) operation
}

Mistake 3: Not Checking for Empty Arrays

# Dangerous
$files = Get-ChildItem -Path "C:\NonExistent"
$files[0]  # Error if path doesn't exist

# Safe
$files = @(Get-ChildItem -Path "C:\MayNotExist" -ErrorAction SilentlyContinue)
if ($files.Count -gt 0) {
    $files[0]
}

Mistake 4: Assuming Type Consistency

# Mixed types can cause issues
$mixed = @(1, "two", $true, 4.5)
$sum = $mixed | ForEach-Object { $_ + 10 }  # Fails on "two"

# Better: Use strongly typed arrays
[int[]]$numbers = 1, 2, 3, 4, 5
$sum = $numbers | ForEach-Object { $_ + 10 }  # Works correctly

Mistake 5: Comparing Arrays with -eq

$array1 = @(1, 2, 3)
$array2 = @(1, 2, 3)

# Wrong: This doesn't work as expected
$array1 -eq $array2  # Returns elements, not $true/$false

# Correct: Compare as strings or use Compare-Object
($array1 -join ",") -eq ($array2 -join ",")  # True

# Or use Compare-Object
$diff = Compare-Object $array1 $array2
$isEqual = $diff.Count -eq 0  # True if equal

Best Practices {#best-practices}

✅ Use Strongly Typed Arrays

# Good: Type safety prevents errors
[string[]]$servers = "DC01", "DC02", "FS01"
[int[]]$ports = 80, 443, 3389

✅ Initialize Arrays Properly

# Good: Explicit empty array
$results = @()

# Better: Use List<T> for dynamic collections
$results = [System.Collections.Generic.List[object]]::new()

✅ Check Bounds Before Access

# Good: Safe array access
if ($index -ge 0 -and $index -lt $array.Count) {
    $value = $array[$index]
}

✅ Use Appropriate Collection Type

# For static data
$daysOfWeek = @("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")

# For dynamic data with frequent changes
$log = [System.Collections.Generic.List[string]]::new()

✅ Clone Arrays When Needed

# Good: Create independent copy
$original = 1, 2, 3, 4, 5
$copy = $original.Clone()
$copy[0] = 999  # Doesn't affect $original

✅ Use -contains for Membership Tests

# Efficient membership test
$servers = @("DC01", "DC02", "FS01")

if ($servers -contains "DC01") {
    Write-Host "DC01 found"
}

✅ Document Array Purpose

# Good: Clear variable names and comments
# List of production domain controllers
$productionDCs = @("DC01", "DC02", "DC03")

# Email recipients for critical alerts
$criticalAlertRecipients = @("admin@contoso.com", "manager@contoso.com")

Advanced Tips and Tricks {#advanced-tips}

Tip 1: Array Splatting

Pass array elements as individual parameters:

# Array splatting
$copyParams = @{
    Path = "C:\Source\file.txt"
    Destination = "C:\Dest\file.txt"
    Force = $true
}

Copy-Item @copyParams

Tip 2: Parallel Processing (PowerShell 7+)

# Process array elements in parallel
$servers = @("DC01", "DC02", "FS01", "SQL01", "WEB01")

$results = $servers | ForEach-Object -Parallel {
    Test-Connection -ComputerName $_ -Count 1 -Quiet
} -ThrottleLimit 5

Tip 3: Array Destructuring

# Destructure array into variables
$data = @("John", "Doe", 30, "Developer")
$firstName, $lastName, $age, $role = $data

Write-Host "$firstName $lastName is a $age year old $role"

Tip 4: Using -split and -join Effectively

# Split string into array
$path = "C:\Users\Admin\Documents\file.txt"
$parts = $path -split "\\"
$filename = $parts[-1]  # file.txt

# Join array into string
$servers = @("DC01", "DC02", "FS01")
$serverList = $servers -join ", "  # "DC01, DC02, FS01"

Tip 5: Array Filtering with .Where() Method

# PowerShell 4.0+ .Where() method (faster than Where-Object)
$numbers = 1..1000

# Filter even numbers
$even = $numbers.Where({ $_ % 2 -eq 0 })

# With options: 'First' to get first N matches
$firstFive = $numbers.Where({ $_ % 2 -eq 0 }, 'First', 5)

Tip 6: Creating Lookup Hashtables from Arrays

# Convert array to hashtable for O(1) lookups
$servers = @("DC01", "DC02", "FS01", "SQL01")

# Create hashtable
$serverLookup = @{}
$servers | ForEach-Object { $serverLookup[$_] = $true }

# Fast lookup
if ($serverLookup["DC01"]) {
    Write-Host "DC01 exists"
}

Tip 7: Array Reduction/Aggregation

# Sum array using Measure-Object
$numbers = 1..100
$sum = ($numbers | Measure-Object -Sum).Sum
Write-Host "Sum: $sum"  # 5050

# Custom aggregation
$total = $numbers | ForEach-Object -Begin { $sum = 0 } -Process { $sum += $_ } -End { $sum }

Frequently Asked Questions {#faqs}

Q: How do I check if a variable is an array?

A: Use the -is operator to check the type:

$var = @(1, 2, 3)

if ($var -is [array]) {
    Write-Host "It's an array with $($var.Count) elements"
}

# Or check specific array type
if ($var -is [int[]]) {
    Write-Host "It's an integer array"
}

Q: What’s the difference between Count and Length?

A: For PowerShell arrays, Count and Length are equivalent. Both return the number of elements. However, .Count is more universally supported across different collection types, so it’s recommended.

$array = @(1, 2, 3, 4, 5)
$array.Count   # 5
$array.Length  # 5

Q: How do I create an array with a specific size?

A: Use the New-Object cmdlet with array type and size:

# Create integer array with 10 elements (initialized to 0)
$array = New-Object int[] 10

# Create string array with 5 elements (initialized to $null)
$strings = New-Object string[] 5

Q: Can I have arrays of different types mixed together?

A: Yes, PowerShell arrays can be heterogeneous (mixed types):

$mixed = @(
    "String",
    42,
    $true,
    (Get-Date),
    @{Name="John"}
)

$mixed.GetType().Name  # Object[]

Q: How do I convert an array to a string?

A: Use the -join operator or [string]::Join():

$array = @("Apple", "Banana", "Orange")

# Join with space
$string = $array -join " "

# Join with comma
$csv = $array -join ", "

# Using .NET method
$string = [string]::Join(", ", $array)

Q: How do I find the largest/smallest value in an array?

A: Use Measure-Object cmdlet:

$numbers = 15, 42, 8, 23, 16, 4, 31

# Get min and max
$stats = $numbers | Measure-Object -Minimum -Maximum
Write-Host "Min: $($stats.Minimum), Max: $($stats.Maximum)"

# Or use [Linq]
[Linq.Enumerable]::Max($numbers)  # 42
[Linq.Enumerable]::Min($numbers)  # 4

Q: How do I remove duplicate values from an array?

A: Use Select-Object -Unique or Get-Unique:

$array = 1, 2, 2, 3, 3, 3, 4, 5, 5

# Method 1: Select-Object (doesn't require sorting)
$unique = $array | Select-Object -Unique

# Method 2: Get-Unique (requires sorted array)
$unique = $array | Sort-Object | Get-Unique

# Method 3: Use hashtable
$unique = $array | Sort-Object -Unique

Q: Can I use negative indices to access array elements from the end?

A: Yes! Negative indices count from the end of the array:

$array = @("First", "Second", "Third", "Fourth", "Fifth")

$array[-1]  # Fifth (last element)
$array[-2]  # Fourth (second to last)
$array[-3]  # Third (third from end)

Q: How do I check if an array contains a specific value?

A: Use the -contains operator:

$servers = @("DC01", "DC02", "FS01", "SQL01")

if ($servers -contains "DC01") {
    Write-Host "DC01 is in the list"
}

# Case-insensitive
if ($servers -icontains "dc01") {
    Write-Host "Found (case-insensitive)"
}

Q: What happens when I assign an array to another variable?

A: You create a reference, not a copy. Both variables point to the same array:

$array1 = @(1, 2, 3)
$array2 = $array1  # Reference, not copy

$array2[0] = 999
$array1[0]  # Also 999!

# To create a copy, use Clone()
$array3 = $array1.Clone()
$array3[0] = 100
$array1[0]  # Still 999

Q: How do I concatenate multiple arrays?

A: Use the + operator:

$array1 = @(1, 2, 3)
$array2 = @(4, 5, 6)
$array3 = @(7, 8, 9)

# Concatenate
$combined = $array1 + $array2 + $array3
$combined  # 1, 2, 3, 4, 5, 6, 7, 8, 9

Q: Can I sort an array in descending order?

A: Yes, use Sort-Object -Descending:

$numbers = 5, 2, 8, 1, 9, 3

# Sort ascending
$ascending = $numbers | Sort-Object
$ascending  # 1, 2, 3, 5, 8, 9

# Sort descending
$descending = $numbers | Sort-Object -Descending
$descending  # 9, 8, 5, 3, 2, 1

Q: How do I flatten a nested array?

A: Use recursion or the -join and -split approach:

$nested = @(
    @(1, 2),
    @(3, @(4, 5)),
    @(6, 7, @(8, 9))
)

# Method 1: Recursive function
function Flatten-Array ($array) {
    $result = @()
    foreach ($item in $array) {
        if ($item -is [array]) {
            $result += Flatten-Array $item
        } else {
            $result += $item
        }
    }
    return $result
}

$flat = Flatten-Array $nested
$flat  # 1, 2, 3, 4, 5, 6, 7, 8, 9

Q: What’s the most efficient way to add many items to an array?

A: Use List<T> instead of array with +=:

# Slow (O(n²) complexity)
$array = @()
1..1000 | ForEach-Object { $array += $_ }

# Fast (O(n) complexity)
$list = [System.Collections.Generic.List[int]]::new()
1..1000 | ForEach-Object { $list.Add($_) }

# Convert back to array if needed
$array = $list.ToArray()

Q: How do I randomize/shuffle an array?

A: Use Get-Random with the array:

$array = 1..10

# Shuffle array
$shuffled = $array | Get-Random -Count $array.Count

$shuffled  # Random order: 3, 7, 1, 9, 2, 5, 10, 4, 8, 6

Conclusion

PowerShell arrays are fundamental data structures essential for efficient scripting and automation. In this comprehensive guide, you’ve learned:

Creating arrays using multiple methods (@(), comma-separated, ranges, cmdlet output) ✅ Accessing elements with positive and negative indices, slicing, and safe access patterns ✅ Adding and removing elements efficiently with ArrayList and ListArray properties and methods for sorting, reversing, finding, and manipulating data ✅ Looping techniques for maximum performance ✅ String arrays and nested arrays for complex data structures ✅ Best practices to avoid common pitfalls and optimize performance ✅ Real-world use cases for server management, user provisioning, and batch operations

Key Takeaways:

  • Use standard arrays for static data with known size
  • Use List<T> for dynamic collections with frequent modifications
  • Always check array bounds before accessing elements
  • Prefer foreach loops for best performance
  • Use strongly typed arrays when possible for type safety

Core Data Structures

Iteration & Processing

Control Flow & Logic

File Operations

Data Export & Conversion

Functions & Parameters

System Administration

Active Directory

Performance & Optimization

  • PowerShell Performance Tuning - Array optimization
  • PowerShell Benchmarking - Performance testing

Comprehensive Guides

  • Complete PowerShell Guide - Full PowerShell with arrays section
  • Complete PowerShell Tutorial - Comprehensive course
  • PowerShell Tutorial Complete - Full tutorial

You can find more topics about Active Directory tools and PowerShell automation on the ActiveDirectoryTools home page.