C#
List<T>
data structures
order preservation
programming best practices

Does a ListT guarantee that items will be returned in the order they were added?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In .NET, List<T> preserves element order during normal iteration, and that order initially matches insertion order. Confusion usually appears when code later sorts, inserts at specific indexes, or mutates the list concurrently. The key is understanding what operations preserve sequence and what operations intentionally change it.

Basic Ordering Behavior of List<T>

List<T> is a dynamic array. Each Add appends at the end, so iteration reflects insertion order.

csharp
1using System;
2using System.Collections.Generic;
3
4class Program
5{
6    static void Main()
7    {
8        var list = new List<string>();
9        list.Add("first");
10        list.Add("second");
11        list.Add("third");
12
13        foreach (var item in list)
14            Console.WriteLine(item);
15    }
16}

Output order is first, second, third.

Capacity Growth Does Not Reorder Items

As List<T> grows, internal capacity can expand. This reallocates the internal array but does not change logical order.

csharp
1var values = new List<int>(capacity: 1);
2for (int i = 0; i < 100; i++)
3    values.Add(i);
4
5Console.WriteLine(values[0]);
6Console.WriteLine(values[99]);

Capacity is an implementation detail; index ordering remains stable unless you run reordering operations.

Operations That Change Sequence

List<T> preserves current order, but some methods intentionally alter it.

Examples:

  • 'Sort'
  • 'Reverse'
  • 'Insert at lower index'
  • 'RemoveAt which shifts following items left'
csharp
1var nums = new List<int> { 3, 1, 2 };
2nums.Sort();
3Console.WriteLine(string.Join(",", nums)); // 1,2,3
4
5nums.Reverse();
6Console.WriteLine(string.Join(",", nums)); // 3,2,1

After these operations, enumeration still follows list order, but the order is no longer original insertion order.

Comparison With Other Collections

If order matters, choosing the right collection is critical.

  • 'List<T> preserves order and supports indexing.'
  • 'HashSet<T> does not promise insertion order.'
  • 'Queue<T> preserves FIFO order with enqueue and dequeue semantics.'
  • 'LinkedList<T> preserves sequence but has different performance tradeoffs.'

Use List<T> when random index access and stable sequence are both important.

Concurrency and Order Assumptions

List<T> is not thread-safe for concurrent writes. If multiple threads add or remove without synchronization, observed results can become unpredictable or throw exceptions.

csharp
1private readonly object _sync = new object();
2private readonly List<int> _events = new();
3
4public void AddEvent(int value)
5{
6    lock (_sync)
7    {
8        _events.Add(value);
9    }
10}

Ordering guarantees assume correct synchronization in multithreaded code.

API Contract Considerations

If your method returns IEnumerable<T> backed by a list, callers often assume ordering is meaningful. Document ordering semantics explicitly.

csharp
1public IEnumerable<Order> GetRecentOrders()
2{
3    // explicitly sorted descending by CreatedAt
4    return _orders.OrderByDescending(o => o.CreatedAt).ToList();
5}

If order matters to consumers, encode that guarantee in tests and documentation.

Serialization Behavior

JSON serializers preserve list element order. If order is significant in API payloads, list-based models are suitable. Be careful when converting to set or dictionary structures in the pipeline, because those types may not preserve the same sequence semantics.

Performance Notes

List<T> provides:

  • fast append in amortized constant time
  • fast index access in constant time
  • slower inserts and removals near front due to element shifts

If your workload requires frequent front insertions, evaluate alternatives like LinkedList<T> or deque-style data structures.

Common Pitfalls

A common pitfall is assuming insertion order survives calls to Sort or Reverse. Another is relying on list order while mutating from multiple threads without locks. Teams sometimes replace List<T> with hash-based collections and unknowingly lose sequence guarantees. API methods also return ordered data without documenting that order, causing fragile consumer assumptions. Finally, front-heavy insert patterns can create performance issues when List<T> is used blindly.

Summary

  • 'List<T> preserves current sequence order during iteration.'
  • Initial order is insertion order when using append operations.
  • Internal resizing does not change logical order.
  • Sorting, reversing, and indexed inserts can change sequence.
  • Concurrency requires synchronization to keep behavior predictable.
  • Document and test ordering guarantees when APIs depend on them.

Course illustration
Course illustration

All Rights Reserved.