Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List shows 4 items in debugger even if filled with exactly one element

When I look at list populated with single item in debugger its _items field contains 4 elements. Can you explain the behavior?

I've found that while debugging my console application to learn about Distinct and ToList and result confuses me. Code:

List<int> nums = new List<int>() { 6, 6, 6, 6, 6, 6, 6, 6 };
List<int> distinctNums = nums.Distinct().ToList();
int[] distinctNums2 = nums.Distinct().ToArray();
  • distinctNums has 4 elements in _items : (6, 0, 0, 0) which is clearly wrong.
  • distinctNums2 has 1 item (6) which is correct.
like image 323
user70267 Avatar asked Nov 20 '13 17:11

user70267


3 Answers

This will create the list distinctNums that contains only one item, just as you should expect. However, lists in .NET are backed by an array which is automatically resized as you add items to it. This array starts off with a size of 4, so the internal array would be [4, 0, 0, 0] if you happen to probe it via reflection or in a debugger.

But if you check, you would find distinctNums.Count == 1. Compare Count and Capacity properties.

like image 78
p.s.w.g Avatar answered Nov 06 '22 19:11

p.s.w.g


Both distinctNums and distinctNums2 really just contain one object.

The list with four items that you're seeing in the debugger is only the array that backs the List<int>. You can see that its Count is 1 and that if you iterate the list, it only returns a single 6.

like image 3
Tim S. Avatar answered Nov 06 '22 21:11

Tim S.


List<T> is an abstraction on top of T[] (a standard array). The size of the array doesn't matter. When you allocate a List<T> with one item the compiler does not allocate new T[1], it gives you some room to grow (it has to reallocate a new array and copy items over when you outgrow the original one which is a relatively expensive operation). Count gives you the lists length and if you call it on that list you will get 1. The other three indexes have the default value of zero and are not part of your list, they're part of the underlying array. They're not relevant. If you tried to do myList[3] you would still get an exception, it wouldn't just return that 0.

To bring this back to the original question, in both cases Distinct returns and IEnumberable<int> with one item. When you call ToList it allocates a new list, the count is 1, it's underlying data structure is an int[] with a starting length of 4. If you added more items it would grow. In the second example you call ToArray so it allocated a new int[1] and returns the reference to it.

like image 1
evanmcdonnal Avatar answered Nov 06 '22 21:11

evanmcdonnal