tags:

views:

3808

answers:

3

Given a List<T> in c# is there a way to extend it (within its capacity) and set the new elements to null? I'd like something that works like a memset. I'm not looking for sugar here, I want fast code. I known that in C the operation could be done in something like 1-3 asm ops per entry.

The best solution I've found is this:

list.AddRange(Enumerable.Repeat(null, count-list.Count));

however that is c# 3.0 (<3.0 is preferred) and might be generating and evaluating an enumerator.

My current code uses:

while(list.Count < lim) list.Add(null);

so that's the starting point for time cost.

The motivation for this is that I need to set the n'th element even if it is after the old Count.

A: 

Why do you want to do that ? The main advantage of a List is that it can grow as needed, so why do you want to add a number of null or default elements to it ?

Isn't it better that you use an array in this case ?

Frederik Gheysels
IIRC array can't grow (you can make a bigger copy but not grow it)
BCS
List<T> isn't an array. It can grow.
Joel Coehoorn
Indeed, an array can grow, but what is the use of using a list when you want to set it to a number of (empty) elements ?Or, did i just misunderstood the question ?
Frederik Gheysels
I had a similar situation once. I wanted to have direct access to some elements using IDs, but I didn't know how many elements there were in advance. The IDs were not sequential, so I needed the same method the OP is asking for. I have posted my version in an answer.
Hosam Aly
Actually arrays *can* grow! I didn't know that, but I just noticed the `Array.Resize()` method: http://msdn.microsoft.com/en-us/library/bb348051.aspx
Hosam Aly
@Hosam, in that case a Dictionary is usually better
configurator
@configurator: Well, not that much. in my case IDs were usually multiples of 2 (e.g. 2, 4, 6, ...), although not necessarily. And speed was important, thus I chose the List. The number of items is less than 1000, so no big deal. Now I intend to change it to an array after knowing it can be resized.
Hosam Aly
Array.Resize() does not change the size of an array. It creates a new array and copies elements.
Lou Franco
@Lou Franco, you're right. I had thought it could be like `realloc`, but it's not. :(
Hosam Aly
+3  A: 
static IEnumerable<T> GetValues<T>(T value, int count) {
   for (int i = 0; i < count; ++i)
      yield return value;
}

list.AddRange(GetValues<object>(null, number_of_nulls_to_add));

This will work with 2.0+

Mehrdad Afshari
This has the benefit that it fills the list with the required values. But if nulls or zeros are sufficient, simply `list.AddRange(new T[count - list.Count]` would be enough. (And I guess it would perform better at least for small array sizes.)
Hosam Aly
+2  A: 

The simplest way is probably by creating a temporary array:

list.AddRange(new T[size - count]);

Where size is the required new size, and count is the count of items in the list. However, for relatively large values of size - count, this can have bad performance, since it can cause the list to reallocate multiple times.(*) It also has the disadvantage of allocating an additional temporary array, which, depending on your requirements, may not be acceptable. You could mitigate both issues at the expense of more explicit code, by using the following methods:

public static class CollectionsUtil
{
    public static List<T> EnsureSize<T>(this List<T> list, int size)
    {
        return EnsureSize(list, size, default(T));
    }

    public static List<T> EnsureSize<T>(this List<T> list, int size, T value)
    {
        if (list == null) throw new ArgumentNullException("list");
        if (size < 0) throw new ArgumentOutOfRangeException("size");

        int count = list.Count;
        if (count < size)
        {
            int capacity = list.Capacity;
            if (capacity < size)
                list.Capacity = Math.max(size, capacity * 2);

            while (count < size)
            {
                list.Add(value);
                ++count;
            }
        }

        return list;
    }
}

The only C# 3.0 here is the use of the "this" modifier to make them extension methods. Remove the modifier and it will work in C# 2.0.

Unfortunately, I never compared the performance of the two versions, so I don't know which one is better.

Oh, and did you know you could resize an array by calling Array.Resize<T>? I didn't know that. :)

Update:
(*) Using list.AddRange(array) will not cause an enumerator to be used. Looking further through Reflector showed that the array will be casted to ICollection<T>, and the Count property will be used so that allocation is done only once.

Hosam Aly
the "list.AddRange(new T[size - count]);" in the comment looks good. I wonder if the optimizer can avoid the enumerator.
BCS
@BCS, I was wrong, the implementation of `List<T>.AddRange` actually optimizes for this case. I have updated my post.
Hosam Aly