JAN
JAN

Reputation: 21865

List C# capacity is always 0 even though using a constructor with capacity ?

Given this simple synchronization code :

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Mutex
{
    class CalcArrayThreads
    {
        private static System.Threading.Mutex mutex = new System.Threading.Mutex();
        private const int ThreadsNumber = 10;
        private static List<int> list = new List<int>(10);
        public static void SumPartialArray(object index)
        {
            mutex.WaitOne();
            int indexBoxed = (int) index;
            int sum = 0;
            for (int i = indexBoxed; i < indexBoxed + 9; i++)
            {
                sum += i;
            }
             Console.WriteLine(string.Format("Thread : {0} Calculated value : {1}", Thread.CurrentThread.Name, sum));

            // list.Add(sum);
            list[indexBoxed] = sum;
            mutex.ReleaseMutex();        
            // Console.WriteLine(list.Count());
        }
        static void Main(string[] args)
        {
            for (int i = 0; i < ThreadsNumber; i++)
            {
                Thread myThread = new Thread(new ParameterizedThreadStart(SumPartialArray));
                myThread.Name = String.Format("Thread{0}", i + 1);
                myThread.Start(i);
            }
            Console.Read();
        }
    }
}

When I use the line :

list[indexBoxed] = sum;

I get :

Unhandled Exception: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.

Even though the capacity of the list is 10 .

Why ?

Upvotes: 1

Views: 1916

Answers (3)

Grant Winney
Grant Winney

Reputation: 66449

If you just did the following with an array, it'd work as expected, setting the second element:

int[] list = new int[10];

list[2] = 5;  // second element is 5, no exception thrown

Looking at the List<T> constructor, when you pass in a capacity, it's actually doing something very similar using an internal array:

this._items = new T[capacity];

So it seems like it should work then, when you try to access any element less than capacity, but here's how the indexer is implemented:

public T this[int index]
{
    get
    {
        if (index >= this._size)
            throw new ArgumentOutOfRangeException("...");
        return this._items[index];
    }
    set
    {
        if (index >= this._size)
            throw new ArgumentOutOfRangeException("...");
        this._items[index] = value;
    }
}

It's actually checking a _size variable first, and throwing the exception if the index you're requesting is larger than it. If it weren't for that check, it'd work as you expect.

_size is initialized to 0 unless you pass a non-empty collection into the constructor, and changes value when you use the Add, RemoveAt, Clear, etc methods. Internally, it's still just using an array to store the elements, and if _size is greater than the capacity (say, after trying to Add one more element), it allocates a new array and copies all the elements from the older (smaller) array into it.

I see two solutions you could consider using:

Either just use an array, like this:

private static int[] list = new int[10];

Or supply a collection of some default value (here, a bunch of zeroes) via the constructor:

private static List<int> list = new List<int>(Enumerable.Repeat(0, 10));

Upvotes: 4

Magesh Kumaar
Magesh Kumaar

Reputation: 511

Since you have not added anything to the list yet. It will always be 0.

You actually need to use Count property to get the number of elements in the list.

A remark from MSDN:

Capacity is the number of elements that the List can store before resizing is required, whereas Count is the number of elements that are actually in the List.

Capacity is always greater than or equal to Count. If Count exceeds Capacity while adding elements, the capacity is increased by automatically reallocating the internal array before copying the old elements and adding the new elements.

Upvotes: 2

Mihai Caracostea
Mihai Caracostea

Reputation: 8466

The value you are passing in the constructor is the initial capacity which then is increased dynamically as the list grows.

But the list is still empty. And using the index accessor on an empty list will yield the exact exception you are seeing.

To add elements to the list use Add method instead and don't worry about the capacity. It grows internally.

EDIT: As to the title of your question, I belive that the Count is allways 0 (you are not adding elements to it) and not the Capacity.

Upvotes: 1

Related Questions