{"id":200,"date":"2022-02-27T14:38:13","date_gmt":"2022-02-27T14:38:13","guid":{"rendered":"https:\/\/baldsolutions.com\/?p=200"},"modified":"2022-02-27T14:38:34","modified_gmt":"2022-02-27T14:38:34","slug":"add-vs-addrange-speed-up-your-lists-concatenation","status":"publish","type":"post","link":"https:\/\/baldsolutions.com\/index.php\/2022\/02\/27\/add-vs-addrange-speed-up-your-lists-concatenation\/","title":{"rendered":"Add vs AddRange &#8211; speed up your lists concatenation"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Introduction<\/h1>\n\n\n\n<p>Adding elements to a list seems like a pretty common operation. The most intuitive solutions is to invoke some kind of &#8220;Add&#8221; method on the list object. The <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"List\">List<\/a> class exposes two methods: <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.add?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Add \">Add <\/a>and <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.addrange?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"AddRange\">AddRange<\/a>. 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 <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.add?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Add \">Add <\/a> method, or adding whole using <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.addrange?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"AddRange\">AddRange<\/a>? It turns out the difference between the approaches might be significant.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Relocation<\/h1>\n\n\n\n<p>Let&#8217;s consider following code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public List&lt;int> AddOnebyOne(List&lt;int> list)\n{\n    foreach (var item in list)\n        _list.Add(item);\n\n    return _list;\n}\n\npublic List&lt;int> AddRange(List&lt;int> list)\n{\n    _list.AddRange(list);\n\n    return _list;\n}<\/code><\/pre>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>The <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1?view=net-5.0\" title=\"List\">List<\/a> class stores elements in its internal structure. The internal structure has its capacity that can be accessed by the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.capacity?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Capacity\">Capacity<\/a> property. If you define the List object&#8217;s capacity (using this <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.-ctor?view=net-5.0#system-collections-generic-list-1-ctor(system-int32)\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"constructor\">constructor<\/a>)  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&#8217;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.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Add vs AddRange<\/h1>\n\n\n\n<p>The <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.addrange?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"AddRange\">AddRange<\/a> has one important advantage over the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.add?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Add \">Add <\/a> method when it comes to lists concatenation. If you go to the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.addrange?view=net-5.0#remarks\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Remarks\">Remarks<\/a> section you can read: &#8220;If the new\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.count?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\">Count<\/a>\u00a0(the current\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.count?view=net-5.0\">Count<\/a>\u00a0plus the size of the collection) will be greater than\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.capacity?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\">Capacity<\/a>, the capacity of the\u00a0<a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\">List&lt;T><\/a>\u00a0is 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.&#8221;. Thanks to that you can avoid many relocations and have only one.  <\/p>\n\n\n\n<p>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&#8217;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.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Performance<\/h1>\n\n\n\n<p>Now, let&#8217;s take a look at the benchmark results.  The &#8220;listSize&#8221; is the size of the base list that the elements are added to. The &#8220;listToAddSize&#8221; is the size of the list whose elements are added to the base list.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"665\" height=\"287\" src=\"https:\/\/baldsolutions.com\/wp-content\/uploads\/2022\/02\/image-6.png\" alt=\"\" class=\"wp-image-201\" srcset=\"https:\/\/baldsolutions.com\/wp-content\/uploads\/2022\/02\/image-6.png 665w, https:\/\/baldsolutions.com\/wp-content\/uploads\/2022\/02\/image-6-300x129.png 300w\" sizes=\"auto, (max-width: 665px) 100vw, 665px\" \/><\/figure>\n\n\n\n<p>The results show that using <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.addrange?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"AddRange\">AddRange<\/a>, when it comes to lists concatenation, can speed up the concatenation ~2 times.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Summary<\/h1>\n\n\n\n<p>To sum up, <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.collections.generic.list-1.addrange?view=net-5.0\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"AddRange\">AddRange<\/a> 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 &#8220;just in case&#8221; 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.<\/p>\n\n\n\n<p>As always I&#8217;ve prepared an example and you can find it on my <a href=\"https:\/\/github.com\/tglowka\/baldsolutions\/tree\/master\/.NET\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"github\">github<\/a>, project: <a href=\"https:\/\/github.com\/tglowka\/baldsolutions\/tree\/master\/.NET\/Add-vs-AddRange\" target=\"_blank\" rel=\"noreferrer noopener\">Add-vs-AddRange<\/a>, test project: <a href=\"https:\/\/github.com\/tglowka\/baldsolutions\/tree\/master\/.NET\/Add-vs-AddRange-tests\" target=\"_blank\" rel=\"noreferrer noopener\">Add-vs-AddRange-tests<\/a>, benchmark project: <a href=\"https:\/\/github.com\/tglowka\/baldsolutions\/tree\/master\/.NET\/Add-vs-AddRange-benchmark\" target=\"_blank\" rel=\"noreferrer noopener\">Add-vs-AddRange-benchmark<\/a>. I encourage you to go through the code, debug it and try to test your own scenarios.<\/p>\n\n\n\n<p>Have a nice day, bye!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Adding elements to a list seems like a pretty common operation. The most intuitive solutions is to invoke some kind of &#8220;Add&#8221; method on&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/baldsolutions.com\/index.php\/2022\/02\/27\/add-vs-addrange-speed-up-your-lists-concatenation\/\">Continue reading<span class=\"screen-reader-text\">Add vs AddRange &#8211; speed up your lists concatenation<\/span><\/a><\/div>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[2],"tags":[],"class_list":["post-200","post","type-post","status-publish","format-standard","hentry","category-net","entry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/posts\/200","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/comments?post=200"}],"version-history":[{"count":9,"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/posts\/200\/revisions"}],"predecessor-version":[{"id":211,"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/posts\/200\/revisions\/211"}],"wp:attachment":[{"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/media?parent=200"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/categories?post=200"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/baldsolutions.com\/index.php\/wp-json\/wp\/v2\/tags?post=200"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}