Skip to content

Add vs AddRange – speed up your lists concatenation

Introduction

Adding elements to a list seems like a pretty common operation. The most intuitive solutions is to invoke some kind of “Add” method on the list object. The List class exposes two methods: Add and AddRange. First one adds an element, second adds range of elements. Nothing revolutionary here. However, if you want to concatenate two lists which approach would you choose: adding one by one using Add method, or adding whole using AddRange? It turns out the difference between the approaches might be significant.

Relocation

Let’s consider following code:

public List<int> AddOnebyOne(List<int> list)
{
    foreach (var item in list)
        _list.Add(item);

    return _list;
}

public List<int> AddRange(List<int> list)
{
    _list.AddRange(list);

    return _list;
}

We have two methods: AddOneByOne and AddRange. Their implementation is straightforward: AddOneByOne concatenate two lists by adding elements one by one and AddRange adds whole list. The outcome in both cases is the same. The main difference is what happens behind the scenes.

The List class stores elements in its internal structure. The internal structure has its capacity that can be accessed by the Capacity property. If you define the List object’s capacity (using this constructor) then the internal array will have the size of the passed capacity. Everything works great until you want to add an element when your list’s internal array is full. Such a scenario causes relocation. The list has to allocate new array with greater capacity and then move all elements there.

Add vs AddRange

The AddRange has one important advantage over the Add method when it comes to lists concatenation. If you go to the Remarks section you can read: “If the new Count (the current Count plus the size of the collection) will be greater than Capacity, the capacity of the List<T> is increased by automatically reallocating the internal array to accommodate the new elements, and the existing elements are copied to the new array before the new elements are added.”. Thanks to that you can avoid many relocations and have only one.

Now, think of the list concatenation in terms of aforementioned relocation. The Add method adds elements one by one until it surpasses the internal array’s size. Once the array is full the relocation happen. If the concatenated list is long such a relocation will occur many times, therefore decreasing the overall performance of concatenation.

Performance

Now, let’s take a look at the benchmark results. The “listSize” is the size of the base list that the elements are added to. The “listToAddSize” is the size of the list whose elements are added to the base list.

The results show that using AddRange, when it comes to lists concatenation, can speed up the concatenation ~2 times.

Summary

To sum up, AddRange method might speed up your program execution time when the program consists of some kind of lists concatenation operations. One of the good practice is to define the initial list capacity that is big enough to store all elements throughout the program execution. However, DO NOT create list with to big capacity “just in case” because it is just memory waste. Dynamic relocation is not a bad thing as it adjusts your data structure the to particular scenario, so you should not be afraid of instantiating the list object without the initial capacity. As always it all depends on your particular case which approach is the one you should use.

As always I’ve prepared an example and you can find it on my github, project: Add-vs-AddRange, test project: Add-vs-AddRange-tests, benchmark project: Add-vs-AddRange-benchmark. I encourage you to go through the code, debug it and try to test your own scenarios.

Have a nice day, bye!

Published in.NET

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *