175

.NET offers a generic list container whose performance is almost identical (see Performance of Arrays vs. Lists question). However they are quite different in initialization.

Arrays are very easy to initialize with a default value, and by definition they already have certain size:

string[] Ar = new string[10];

Which allows one to safely assign random items, say:

Ar[5]="hello";

with list things are more tricky. I can see two ways of doing the same initialization, neither of which is what you would call elegant:

List<string> L = new List<string>(10);
for (int i=0;i<10;i++) L.Add(null);

or

string[] Ar = new string[10];
List<string> L = new List<string>(Ar);

What would be a cleaner way?

EDIT: The answers so far refer to capacity, which is something else than pre-populating a list. For example, on a list just created with a capacity of 10, one cannot do L[2]="somevalue"

EDIT 2: People wonder why I want to use lists this way, as it is not the way they are intended to be used. I can see two reasons:

  1. One could quite convincingly argue that lists are the "next generation" arrays, adding flexibility with almost no penalty. Therefore one should use them by default. I'm pointing out they might not be as easy to initialize.

  2. What I'm currently writing is a base class offering default functionality as part of a bigger framework. In the default functionality I offer, the size of the List is known in advanced and therefore I could have used an array. However, I want to offer any base class the chance to dynamically extend it and therefore I opt for a list.

Félix LD
  • 372
  • 1
  • 5
  • 19
Boaz
  • 25,331
  • 21
  • 69
  • 77
  • 1
    "EDIT: The answers so far refer to capacity, which is some else than pre-populating a list. For example, on a list just created with a capacity 10, one can not do L[2]="somevalue"" Given this modification, perhaps you should reword the Question Title... – aranasaurus Jan 21 '09 at 21:03
  • But, what's the use of pre-populating a list with empty values, cause that's what the topicstarter is trying to do ? – Frederik Gheysels Jan 21 '09 at 21:04
  • Frederik: Exactly. When would this be necessary...ever? – Ed S. Jan 21 '09 at 21:05
  • 1
    If positional mapping is that crucial, wouldn't it make more sense to use a Dictionary? – Greg D Jan 23 '09 at 13:28
  • @Boaz: I don't think you understand the concepts at work here. – GEOCHET Jan 27 '09 at 17:19
  • I've ended up doing using List like this instead of Dictionary for quick and dirty projects in Unity because Lists work out of the box with Unity inspector. – Rudi Jan 10 '13 at 17:55
  • 1
    Almost no penalty is not no penalty. .NET has both List and Array for a reason. – paparazzo Apr 07 '13 at 15:07
  • 9
    `List` is not a replacement for `Array`. They solve distinctly separate problems. If you want a fixed size, you want an `Array`. If you use a `List`, you are Doing It Wrong. – Zenexer Nov 29 '13 at 10:26
  • 1
    "One could quite convincingly argue that lists are the "next generation" arrays, adding flexibility with almost no penalty" - well one is wrong :) can you create a multi-dimensional list? the equivalent to `string[] s=new string[5,5,5,5,5]` (and not `=new string[][][][][]`) - nope.. a List is a List.. if you want the next generation of arrays you will need to develop it. – G.Y Dec 24 '13 at 17:57
  • However, does List avoid the single object size limit or Array? This would be a case to use a fixed length List (then use Parallelism to populate it). – Gary May 22 '17 at 17:57
  • 14
    I always find answers that tries to hammer in arguments like "I can't see why I would ever need ..." aggravating. It only means just that: you couldn't see it. It doesn't necessarily mean anything else. I respect that people want to suggest better approaches to a problem, but it should be phrased more humbly, e.g. "Are you sure you need a list? Perhaps if you told us more about your problem...". This way it becomes pleasant, engaging, *and* encourages the OP to improve their question. Be a winner - be humble. – AnorZaken Jun 18 '17 at 17:48
  • My use-case for needing this is working within the constraints of collections in MVC. The fields are output using either an index or what looks like a GUID in the HTML field name/ID: `` or `. I'm tasked with dynamically adding new inputs for additional items as they are added, and since indexes are being used on the form currently and I don't know whether I can mix indexes and GUID's I'm opting for KISS and just outputting additional indexes. For this, I need the list large enough. – bambams Oct 01 '19 at 20:23
  • 2
    … The arguments on this question is weird. People saying "this goes against why lists are created" really have no imagination. Case in point, I need a list of default values upon initialization, and then some outside factor adds on to the list immediately after. – Knight0fDragon Feb 02 '20 at 03:39

16 Answers16

194
List<string> L = new List<string> ( new string[10] );
  • 3
    personally I think this is the cleanest way - although it was mentioned in the question body as potentially clumsy - don't know why – hello_earth Sep 03 '13 at 12:14
  • 4
    +1: Just hit a situation where I needed a variable size list to be initialised with a fixed set of nulls before populating via an index (and adding extras afterwards, so an array was unsuitable). This answer gets my vote for being practical and simple. – iCollect.it Ltd Feb 26 '14 at 11:53
  • Fabulous answer. @GoneCoding I was just wondering whether internally the newly initialized list `L` will simply reuse the memory of `string[10]` as is (backing store of an lists is also an array) or it will allocate a new memory of its own and then copy the contents of `string[10]`? `string[10]` will get garbage collected automatically if `L` selects the later route. – RBT Mar 25 '17 at 04:30
  • 3
    @RBT That is the only downside to this approach: The array will *not* be reused, so you will momentarily have two copies of the array (List uses arrays internally as you seem to be aware of). In rare cases that might be a problem if you have a *huge* number of elements, or memory constraints. And yes the extra array will be eligible for garbage collection as soon as the List constructor is done (it would have been a horrible memory leak otherwise). Note that eligible does not mean "collected right away", but rather the next time garbage collection runs. – AnorZaken Jun 18 '17 at 17:30
  • 5
    This answer allocates 10 new strings - then iterates over them copying them as required. If you're working with large arrays; then don't even consider this as it needs twice the memory than the accepted answer. – UKMonkey Jun 04 '19 at 15:08
103

I can't say I need this very often - could you give more details as to why you want this? I'd probably put it as a static method in a helper class:

public static class Lists
{
    public static List<T> RepeatedDefault<T>(int count)
    {
        return Repeated(default(T), count);
    }

    public static List<T> Repeated<T>(T value, int count)
    {
        List<T> ret = new List<T>(count);
        ret.AddRange(Enumerable.Repeat(value, count));
        return ret;
    }
}

You could use Enumerable.Repeat(default(T), count).ToList() but that would be inefficient due to buffer resizing.

Note that if T is a reference type, it will store count copies of the reference passed for the value parameter - so they will all refer to the same object. That may or may not be what you want, depending on your use case.

EDIT: As noted in comments, you could make Repeated use a loop to populate the list if you wanted to. That would be slightly faster too. Personally I find the code using Repeat more descriptive, and suspect that in the real world the performance difference would be irrelevant, but your mileage may vary.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    I realize this is an old post, but I am curious. Enumerable.Repeat fares much worse compared to a for loop, as per the last portion of the link (http://www.dotnetperls.com/initialize-array). Also AddRange() has O(n) complexity as per msdn. Isn't it a bit counter productive to use the given solution instead of a simple loop? – Jimmy Jan 23 '14 at 11:16
  • 1
    @Jimmy: Both approaches will be O(n), and I find this approach to be more descriptive of what I'm trying to achieve. If you prefer a loop, feel free to use it. – Jon Skeet Jan 23 '14 at 11:18
  • 1
    @Jimmy: Also note that the benchmark there is using `Enumerable.Repeat(...).ToArray()`, which is *not* how I'm using it. – Jon Skeet Jan 23 '14 at 11:20
  • @JonSkeet, I see that now. I did run some tests now, and for int lists the for loop runs slightly faster, but for decimal list your solution with repeat works slightly faster. – Jimmy Jan 23 '14 at 11:39
  • Don't really see the need for two separate overloads. Just declare it as `public static List Repeated(int count, TSource value = default(T))` instead. – AnorZaken Jun 18 '17 at 17:21
  • @AnorZaken: Well it's not really an overload given that it's a separate method name - and bear in mind that this answer was written in January 2009, before C# even had optional parameters... – Jon Skeet Jun 18 '17 at 19:17
  • @JonSkeet technically correct as always. :P Well anyway I quite often try to add (what I hope is) useful comments to old answers for newcomers to find. It's not personal criticism. It's just that when googling comes up with old answers, it doesn't hurt to add a modern perspective. And the point is visibility - so for a small change a comment makes more sense than a new answer in my opinion. Because frankly, there's nothing inherently wrong with your answer. It's just a bit dated, as you said. Cheers! :) – AnorZaken Jun 19 '17 at 10:24
  • 2
    I've used the `Enumerable.Repeat()` in the following way (implemented `Pair` just like the C++ equivalent): `Enumerable.Repeat( new Pair(int.MaxValue,-1), costs.Count)` noticing the side effect, that the `List` was full of referenced copies to a single object. Changing an element like `myList[i].First = 1` changed every single element in the whole `List`. It took me hours to find this bug. Do you guys know any solution to this issue (except for just using a common loop and use `.Add(new Pair...)`? – 00zetti Aug 21 '17 at 09:52
  • @00zetti: It's not clear what point your comment is attempting to make. – Jon Skeet Aug 21 '17 at 09:53
  • @JonSkeet Sry I missed the Shift button and posted it too early - see the edit ^^ – 00zetti Aug 21 '17 at 09:58
  • @00zetti: At that point, it's effectively asking an unrelated question. It should be in a new post, after checking for duplicates. You probably just want to use `Enumerable.Range(...).Select(...).ToList()` though. – Jon Skeet Aug 21 '17 at 09:59
  • @JonSkeet This worked for me, thanks. I just thought it could be of general interest, since you might do not want references to the same object (what `new` intuitively didn't meaned to me). – 00zetti Aug 21 '17 at 10:17
  • Useful to pad ArrayList when you need to InsertRange padding values so you can insert beyond the current size. By default the ArrayList would throw as it does not pad itself. – David Burg Sep 05 '17 at 22:00
  • @JeremyRayBrown : I noticed that you were trying to add a long edit to the existing answer. I'd suggest adding another answer, and comment here on this one with a link to your proposal. – Pac0 Jul 25 '19 at 14:44
  • 1
    @Pac0, I just added an answer below. The edits can be disapproved. – Jeremy Ray Brown Jul 25 '19 at 14:53
19

Use the constructor which takes an int ("capacity") as an argument:

List<string> = new List<string>(10);

EDIT: I should add that I agree with Frederik. You are using the List in a way that goes against the entire reasoning behind using it in the first place.

EDIT2:

EDIT 2: What I'm currently writing is a base class offering default functionality as part of a bigger framework. In the default functionality I offer, the size of the List is known in advanced and therefore I could have used an array. However, I want to offer any base class the chance to dynamically extend it and therefore I opt for a list.

Why would anyone need to know the size of a List with all null values? If there are no real values in the list, I would expect the length to be 0. Anyhow, the fact that this is cludgy demonstrates that it is going against the intended use of the class.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • 62
    This answer does not allocate 10 null entries in the list (which was the requirement), it simply allocates space for 10 entries *before a resize of the list is required* (i.e. capacity), so this does nothing different to `new List()` as far as the problem goes. Well done on getting so many up-votes though :) – iCollect.it Ltd Feb 26 '14 at 11:47
  • 2
    that overloaded constructor is the "initial capacity" value not the "size" or "length", and it doesn't initialise the items either – Matt Wilko Nov 27 '14 at 12:07
  • 2
    To answer "why would someone need this": I need this right now for deep cloning tree data structures. A node might or might not populate its children, yet my base node class needs to be able to clone itself with all it's child nodes. Blah, blah it's even more complicated. **But I need both to populate my still empty list via `list[index] = obj;` and to use some other list capabilities.** – Bitterblue Feb 04 '15 at 10:14
  • 3
    @Bitterblue: Also, any sort of pre-allocated mapping where you may not have all of the values up front. There are certainly uses; I wrote this answer in my less experienced days. – Ed S. Feb 04 '15 at 19:29
  • correct me if i'm wrong but this would be useful for optimizing the List.Add Method by avoid resizing the underlying store each time Add is called individually? – Shayan Zafar Aug 19 '22 at 14:43
  • @ShayanZafar .....Then literally use an array. You use lists to account for potential re-sizing. If the concern is optimization then you might as well discard the overhead that a `List` comes with also – Narish Apr 17 '23 at 15:39
12

Create an array with the number of items you want first and then convert the array in to a List.

int[] fakeArray = new int[10];

List<int> list = fakeArray.ToList();
mini998
  • 511
  • 1
  • 5
  • 11
10

If you want to initialize the list with N elements of some fixed value:

public List<T> InitList<T>(int count, T initValue)
{
  return Enumerable.Repeat(initValue, count).ToList();
}
Amy B
  • 108,202
  • 21
  • 135
  • 185
6

This is an old question, but I have two solutions. One is fast and dirty reflection; the other is a solution that actually answers the question (set the size not the capacity) while still being performant, which none of the answers here do.


Reflection

This is quick and dirty, and should be pretty obvious what the code does. If you want to speed it up, cache the result of GetField, or create a DynamicMethod to do it:

public static void SetSize<T>(this List<T> l, int newSize) =>
    l.GetType().GetField("_size", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(l, newSize);

Obviously a lot of people will be hesitant to put such code into production.


ICollection<T>

This solution is based around the fact that the constructor List(IEnumerable<T> collection) optimizes for ICollection<T> and immediately adjusts the size to the correct amount, without iterating it. It then calls the collections CopyTo to do the copy.

The code for the List<T> constructor is as follows:

public List(IEnumerable<T> collection) {
....
    ICollection<T> c = collection as ICollection<T>;
    if (collection is ICollection<T> c)
    {
        int count = c.Count;
        if (count == 0)
        {
            _items = s_emptyArray;
        }
        else {
            _items = new T[count];
            c.CopyTo(_items, 0);
            _size = count;
        }
    }    

So we can completely optimally pre-initialize the List to the correct size, without any extra copying.

How so? By creating an ICollection<T> object that does nothing other than return a Count. Specifically, we will not implement anything in CopyTo which is the only other function called.

private struct SizeCollection<T> : ICollection<T>
{
    public SizeCollection(int size) =>
        Count = size;

    public void Add(T i){}
    public void Clear(){}
    public bool Contains(T i)=>true;
    public void CopyTo(T[]a, int i){}
    public bool Remove(T i)=>true;
    public int Count {get;}
    public bool IsReadOnly=>true;
    public IEnumerator<T> GetEnumerator()=>null;
    IEnumerator IEnumerable.GetEnumerator()=>null;
}

public List<T> InitializedList<T>(int size) =>
    new List<T>(new SizeCollection<T>(size));

We could in theory do the same thing for AddRange/InsertRange for an existing array, which also accounts for ICollection<T>, but the code there creates a new array for the supposed items, then copies them in. In such case, it would be faster to just empty-loop Add:

public void SetSize<T>(this List<T> l, int size)
{
    if(size < l.Count)
        l.RemoveRange(size, l.Count - size);
    else
        for(size -= l.Count; size > 0; size--)
            l.Add(default(T));
}
Charlieface
  • 52,284
  • 6
  • 19
  • 43
  • Seeing that IEnumerable constructor really makes me wish for there to be one accepting a `ReadOnlySpan`... – Ray Jul 25 '22 at 16:58
  • See also https://stackoverflow.com/a/60514418/14868997 and https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.collectionsmarshal.asspan?view=net-5.0 – Charlieface Jul 25 '22 at 20:08
  • Yeah, I've been at that question before I came here. What I need is to initialize the internal buffer and `Count` manually as I already have the buffer in memory (read from a stream). `CollectionsMarshal.AsSpan()` only grabs a `Span` storing all items up to `Count` - which is 0 on new lists. :( – Ray Jul 25 '22 at 20:15
  • So use my solution pre-size it, then grab the array using `CollectionsMarshal` – Charlieface Jul 25 '22 at 20:16
  • It would definitely do its job. But as I need it only once, it's too much workaround, relying on the List implementation (even if it rarely if ever changes), adding a dummy class... I'll keep it kiss and simply iterate over my `Span` and `Add`ing each element to a new `List` - until one day a constructor accepting `Span` may be added :o) – Ray Jul 25 '22 at 20:24
  • Actually now that I think about it, you can just use the `ICollection.CopyTo` function which accepts an array, you can copy the `Span` straight into it. – Charlieface Jul 25 '22 at 20:26
  • That would copy data _from_ the `List` to a Span, I need to make the `Span` the internal, initial buffer of the `List`. – Ray Jul 25 '22 at 20:28
  • Well you could store a delegate that fills the array https://dotnetfiddle.net/QqY3yP – Charlieface Jul 25 '22 at 21:10
  • @Charlieface: You can but then you have to do some hackery indeed to get a span to it. – Joshua Oct 05 '22 at 18:27
  • Depends where you get it from. The fiddle I show creates the span from an array on the outside. I admit this does cause the lambda to capture a variable, but it depends where you are getting it from as to whether that's a problem. Ultimately, this wasn't the original question so didn't go too far trying to work it out. – Charlieface Oct 06 '22 at 01:54
5

Why are you using a List if you want to initialize it with a fixed value ? I can understand that -for the sake of performance- you want to give it an initial capacity, but isn't one of the advantages of a list over a regular array that it can grow when needed ?

When you do this:

List<int> = new List<int>(100);

You create a list whose capacity is 100 integers. This means that your List won't need to 'grow' until you add the 101th item. The underlying array of the list will be initialized with a length of 100.

Frederik Gheysels
  • 56,135
  • 11
  • 101
  • 154
  • 2
    "Why are you using a List if you want to initialize it with a fixed value" A good point. – Ed S. Jan 21 '09 at 20:58
  • 8
    He asked for a list in which each element was initialized and the list had a size, not just a capacity. This answer is incorrect as it stands now. – Nic Foster Sep 18 '15 at 05:56
  • 1
    The question requires an answer, not criticism of being asked. Sometimes there is a reason to do it this way, as opposed to using an array. If interested in why this might be the case, one can ask a Stack Overflow question for it. – Dino Dini Apr 21 '20 at 13:57
3

Initializing the contents of a list like that isn't really what lists are for. Lists are designed to hold objects. If you want to map particular numbers to particular objects, consider using a key-value pair structure like a hash table or dictionary instead of a list.

Welbog
  • 59,154
  • 9
  • 110
  • 123
3

You seem to be emphasizing the need for a positional association with your data, so wouldn't an associative array be more fitting?

Dictionary<int, string> foo = new Dictionary<int, string>();
foo[2] = "string";
Greg D
  • 43,259
  • 14
  • 84
  • 117
2

The accepted answer (the one with the green check mark) has an issue.

The problem:

var result = Lists.Repeated(new MyType(), sizeOfList);
// each item in the list references the same MyType() object
// if you edit item 1 in the list, you are also editing item 2 in the list

I recommend changing the line above to perform a copy of the object. There are many different articles about that:

If you want to initialize every item in your list with the default constructor, rather than NULL, then add the following method:

public static List<T> RepeatedDefaultInstance<T>(int count)
    {
        List<T> ret = new List<T>(count);
        for (var i = 0; i < count; i++)
        {
            ret.Add((T)Activator.CreateInstance(typeof(T)));
        }
        return ret;
    }
Jeremy Ray Brown
  • 1,499
  • 19
  • 23
  • 1
    It's not an "issue" - it's the normal behavior of copying references. Without knowing the situation, you can't know whether it's suitable to copy the object. The question isn't about whether or not lists should be populated with copies - it's about initializing a list with a capacity. I'll clarify my answer in terms of the behavior it *does* have, but I really don't think it counts as a problem in the context of this question. – Jon Skeet Jul 25 '19 at 15:50
  • @JonSkeet, I was replying to the static helper, which is okay for primitive types but not reference types. A scenario where a list is initialized with the same referenced item doesn't seem right. In that scenario why even have the list if every item in it points to the same object on the heap. – Jeremy Ray Brown Jul 25 '19 at 16:16
  • Sample scenario: you want to populate a list of strings, initially with an entry of "Unknown" for every element, then you modify the list for specific elements. In that case it's entirely reasonable for all the "Unknown" values to be references to the same string. It would be pointless to clone the string each time. Populating a string with multiple references to the same object isn't "right" or "wrong" in a general sense. So long as readers know what the behavior is, it's up to them to decide whether it meets *their specific use case*. – Jon Skeet Jul 25 '19 at 17:28
1

You can use Linq to cleverly initialize your list with a default value. (Similar to David B's answer.)

var defaultStrings = (new int[10]).Select(x => "my value").ToList();

Go one step farther and initialize each string with distinct values "string 1", "string 2", "string 3", etc:

int x = 1;
var numberedStrings = (new int[10]).Select(x => "string " + x++).ToList();
Community
  • 1
  • 1
James Lawruk
  • 30,112
  • 19
  • 130
  • 137
1
string [] temp = new string[] {"1","2","3"};
List<string> temp2 = temp.ToList();
Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
Henk
  • 704
  • 6
  • 14
1

After thinking again, I had found the non-reflection answer to the OP question, but Charlieface beat me to it. So I believe that the correct and complete answer is https://stackoverflow.com/a/65766955/4572240

My old answer:

If I understand correctly, you want the List<T> version of new T[size], without the overhead of adding values to it.

If you are not afraid the implementation of List<T> will change dramatically in the future (and in this case I believe the probability is close to 0), you can use reflection:

    public static List<T> NewOfSize<T>(int size) {
        var list = new List<T>(size);
        var sizeField = list.GetType().GetField("_size",BindingFlags.Instance|BindingFlags.NonPublic);
        sizeField.SetValue(list, size);
        return list;
    }

Note that this takes into account the default functionality of the underlying array to prefill with the default value of the item type. All int arrays will have values of 0 and all reference type arrays will have values of null. Also note that for a list of reference types, only the space for the pointer to each item is created.

If you, for some reason, decide on not using reflection, I would have liked to offer an option of AddRange with a generator method, but underneath List<T> just calls Insert a zillion times, which doesn't serve.

I would also like to point out that the Array class has a static method called ResizeArray, if you want to go the other way around and start from Array.

To end, I really hate when I ask a question and everybody points out that it's the wrong question. Maybe it is, and thanks for the info, but I would still like an answer, because you have no idea why I am asking it. That being said, if you want to create a framework that has an optimal use of resources, List<T> is a pretty inefficient class for anything than holding and adding stuff to the end of a collection.

Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46
0

A notice about IList: MSDN IList Remarks: "IList implementations fall into three categories: read-only, fixed-size, and variable-size. (...). For the generic version of this interface, see System.Collections.Generic.IList<T>."

IList<T> does NOT inherits from IList (but List<T> does implement both IList<T> and IList), but is always variable-size. Since .NET 4.5, we have also IReadOnlyList<T> but AFAIK, there is no fixed-size generic List which would be what you are looking for.

EricBDev
  • 1,279
  • 13
  • 21
0

This is a sample I used for my unit test. I created a list of class object. Then I used forloop to add 'X' number of objects that I am expecting from the service. This way you can add/initialize a List for any given size.

public void TestMethod1()
    {
        var expected = new List<DotaViewer.Interface.DotaHero>();
        for (int i = 0; i < 22; i++)//You add empty initialization here
        {
            var temp = new DotaViewer.Interface.DotaHero();
            expected.Add(temp);
        }
        var nw = new DotaHeroCsvService();
        var items = nw.GetHero();

        CollectionAssert.AreEqual(expected,items);


    }

Hope I was of help to you guys.

Abhay Shiro
  • 3,431
  • 2
  • 16
  • 26
-2

A bit late but first solution you proposed seems far cleaner to me : you dont allocate memory twice. Even List constrcutor needs to loop through array in order to copy it; it doesn't even know by advance there is only null elements inside.

1. - allocate N - loop N Cost: 1 * allocate(N) + N * loop_iteration

2. - allocate N - allocate N + loop () Cost : 2 * allocate(N) + N * loop_iteration

However List's allocation an loops might be faster since List is a built-in class, but C# is jit-compiled sooo...

Victor Drouin
  • 597
  • 2
  • 15