Reputation: 9080
What is the appropriate way of copying a list to a new list? And What is the most efficient way of copying a list to a new list?
By efficient, not code efficiency, more in the behind the scenes framework sense.
List<String>List2 = List.ToList();
Or:
List<String>List2 = new List<String>();
foreach (string item in List)
{
List2.Add(item);
}
Update:
What about more efficient IL code?
Upvotes: 12
Views: 18617
Reputation: 8857
Answers that uses new
(including e.g. ToList()
, ToArray()
which internally use new
) when you need a list copy, cause heap allocations & consequent garbage collection. Those are astoundingly bad ways to get "the fastest possible list copy". So you can safely ignore most of the answers here.
new
your lists once off only, at your application startup, ensuring your lists are maximally sized.
int MAX_SIZE = 100000;
List<int> list1 = new List<int>(MAX_SIZE);
List<int> list2 = new List<int>(MAX_SIZE);
Then do this at runtime wherever you need a full copy of the list:
list2.AddRange(list1);
List.AddRange()
uses List.InsertRange()
which uses Array.Copy()
which uses an approach something like memcpy
in C, i.e. it does bulk copies very fast.
The balance here will be choosing your MAX_SIZE
carefully - not so big that it makes your copies slow, not so small that you exceed what your program is likely to need.
Upvotes: 0
Reputation: 38820
Given that List<T>
has an IEnumerable<T>
constructor, I would prefer this form:
List<string> newList = new List<string>(otherList);
Edit
And as Ondrej points out in the decompiled code below, the constructor of List<T>
preallocates the size of the array and copies the contents over. This is going to be much quicker than creating a new list and then iterating over the other list adding items individually, especially as in your 2nd example you're not specifying how many items to preallocate.
Upvotes: 24
Reputation: 673
Tests shows, that the best perfomance have the .ToList()
method (for a list with 21474836 elements it runs in about 48 ms on a laptop Core i5 CPU).
Every other method is slower, and the method, using .Add()
is the worst, while talking about perfomance.
Here is some test code:
class Program
{
static void Main()
{
var list = new List<int>();
for (int i = 0; i < int.MaxValue / 100; i++)
{
list.Add(i);
}
TimeItAccurate(ListCopy_1, list, 10);
TimeItAccurate(ListCopy_2, list, 10);
TimeItAccurate(ListCopy_3, list, 10);
TimeItAccurate(ListCopy_4, list, 10);
TimeItAccurate(ListCopy_5, list, 10);
}
private static List<int> ListCopy_1(List<int> list)
{
var newList = list.ToList();
return newList;
}
private static List<int> ListCopy_2(List<int> list)
{
var newList = new List<int>(list.Count);
foreach (var i in list)
{
newList.Add(i);
}
return newList;
}
private static List<int> ListCopy_3(List<int> list)
{
var newList = new List<int>(list.ToArray());
return newList;
}
private static List<int> ListCopy_4(List<int> list)
{
var newList = new List<int>(list.Count);
newList.AddRange(list);
return newList;
}
private static List<int> ListCopy_5(List<int> list)
{
var newList = new List<int>(list);
return newList;
}
public static void TimeItAccurate<TIn, TResult>(Func<TIn, TResult> func, TIn argument, int iterationsCount)
{
#region Pre-heat
for (int i = 0; i < 10; i++)
{
var t = func.Invoke(argument);
}
#endregion
var stopwatch = new Stopwatch();
var result = default(TResult);
stopwatch.Start();
for (int i = 0; i < iterationsCount; i++)
{
result = func.Invoke(argument);
}
stopwatch.Stop();
Console.WriteLine("Result:\n{4}(...) == {0}\n\n{1} iterations done in {2} ms.\nAverage time: {3:f5} ms.",
result,
iterationsCount,
stopwatch.ElapsedMilliseconds,
stopwatch.ElapsedMilliseconds / (double)iterationsCount,
func.Method.Name);
}
}
And the results:
Result (.ToList()):
ListCopy_1(...) == System.Collections.Generic.List`1[System.Int32]
10 iterations done in 474 ms.
Average time: 47.40000 ms.
Result (for-cycle with .Add()):
ListCopy_2(...) == System.Collections.Generic.List`1[System.Int32]
10 iterations done in 1896 ms.
Average time: 189.60000 ms.
Result (ctor with .ToArray()):
ListCopy_3(...) == System.Collections.Generic.List`1[System.Int32]
10 iterations done in 981 ms.
Average time: 98.10000 ms.
Result (.AddRange()):
ListCopy_4(...) == System.Collections.Generic.List`1[System.Int32]
10 iterations done in 959 ms.
Average time: 95.90000 ms.
Result (new List<int>(list)):
ListCopy_5(...) == System.Collections.Generic.List`1[System.Int32]
10 iterations done in 480 ms.
Average time: 48.00000 ms.
Upvotes: 2
Reputation: 126072
In terms of efficiency, the first is going to be faster. List<T>
's underlying implementation is an ArrayList, so there's a chance you'll have to resize the underlying array when you call .Add
.
On the other hand, .ToList
can determine the correct initial size of the new List
and avoid the reallocation operation that the foreach
technique suffers from.
With this in mind I'd recommend .ToList
. Less code and it will be faster.
Here's a simple program you can run to verify that ToList
is indeed faster:
void Main()
{
List<int> items = new List<int>();
items = Enumerable.Range(0, 1000000).ToList();
CopyWithToList(items);
CopyWithForeach(items);
}
public void CopyWithToList<T>(List<T> list)
{
var sw = Stopwatch.StartNew();
List<T> copy = list.ToList();
sw.Stop();
Console.WriteLine("CopyWithToList: {0}", sw.Elapsed);
}
public void CopyWithForeach<T>(List<T> list)
{
var sw = Stopwatch.StartNew();
List<T> copy = new List<T>();
foreach (T item in list) {
copy.Add(item);
}
sw.Stop();
Console.WriteLine("CopyWithForeach: {0}", sw.Elapsed);
}
Upvotes: 3
Reputation: 22074
What ToList does (shortened):
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source)
{
return new List<TSource>(source);
}
What List ctor does (shortened):
public List(IEnumerable<T> collection)
{
ICollection<T> collection2 = collection as ICollection<T>;
int count = collection2.Count;
this._items = new T[count];
collection2.CopyTo(this._items, 0);
this._size = count;
}
So the ToList() is much more efficient - it does allocate the space first and then copy all items in one step.
Upvotes: 11
Reputation: 35925
You can use List<T>
constructor that takes IEnumerable<T>
List<string> list1 = new List<string>();
// fill list1
List<string> list2 = new List<string>(list1);
Upvotes: 5
Reputation: 4554
I believe that the two examples are identical, the .ToList() implements probably the later.
The best performance would be something like this:
List<String> list2 = new List<String>(list.Count);
foreach(String item in list)
list2.Add(item);
The important part is to create list2 with enough capacity to hold its content.
If you don't need to modify the either list afterwards, the a reference copy is all you need:
List<String> list2 = list;
Upvotes: 1