Insert inside list in C# - c#

I am initializing my list as below -
List<string> lFiles = new List<string>(12);
and now I want to add/insert my string at specific index.
like I am using below -
lFiles.Insert(6,"File.log.6");
it it throwing excepton as - "Index must be within the bounds of the List."
While initializing I have declared capacity of List but still I am not able insert strings at random indexes.
Anybody knows what I am missing??

The constructor that takes an int32 as parameter doesn't add items to the list, it just pre-allocates some capacity for better performances (this is implementation details). In your case, your list is still empty.

You are initializing the capacity of the list (basically setting the initial size of the internal array for performance purposes), but it does not actually add any elements to the list.
The easiest way to check this is try this:
var list1 = new List<int>();
var list2 = new List<int>(12);
Console.WriteLine(list1.Count); //output is 0
Console.WriteLine(list2.Count); //output is 0
This shows that you still don't have any elements in your list.
In order to initialize populate the array with default or blank elements, you need to actually put something into the list.
int count = 12;
int value = 0
List<T> list = new List<T>(count);
list.AddRange(Enumerable.Repeat(value, count));

There is small confusion with list. When you provide some capacity for constructor, it creates internal array of provided size and fills it with default values of T:
public List(int capacity)
{
if (capacity < 0)
throw new ArgumentException();
if (capacity == 0)
this._items = List<T>._emptyArray;
else
this._items = new T[capacity];
}
But list does not treat that default values as items added to list. Yep, that is confusing a little. Memory is allocated for array, but count of items in list still will be zero. You can check it:
List<string> lFiles = new List<string>(12);
Console.WriteLine(lFiles.Count); // 0
Console.WriteLine(lFiles.Capacity); // 12
Count does not returns size of internal data structure, it returns 'logical' size of list (i.e. number of items which was added and not removed):
public int Count
{
get { return this._size; }
}
And size is changed only when you add or remove items to list. E.g.
public void Add(T item)
{
if (this._size == this._items.Length)
this.EnsureCapacity(this._size + 1); // resize items array
this._items[this._size++] = item; // change size
this._version++;
}
When you are inserting some item at specific index, list does not checks if enough space allocated for items array (well it checks, but just for resizing inner array if current capacity is not enough). List verifies that there is enough items already contained in list (i.e. added, but not removed):
public void Insert(int index, T item)
{
if (index > this._size) // here you get an exception, because size is zero
throw new ArgumentOutOfRangeException();
if (this._size == this._items.Length)
this.EnsureCapacity(this._size + 1); // resize items
if (index < this._size)
Array.Copy(_items, index, this._items, index + 1, this._size - index);
this._items[index] = item;
this._size++;
this._version++;
}

The capacity is just a hint how many elements to expect. There are still no elements in your list.

I think you might want to use a new Dictionary<int, string>(), not a list.
That will let you use the int as a key to set and look up values by:
Otherwise, if you want to use position-based "list", you should just use an string-array instead (but note that that will not let you adjust the size automatically):
var arr = new string[12];
arr[6] = "string at position 6";

Related

How to Insert an object in a list at custom index

I have an empty list defined to hold 120 values, I want to insert an element at index(45), even though the list is currently empty. Is this possible?
public List<Ticket> Tickets = new List<Ticket>(120);
Tickets.Insert(45,ticket); // Here I am getting the ArgumentOutOfRangeException
You set the initial internal capacity of the list to 120. The list is still empty.
List<T> can hold any number of items. Internally, it uses an array to store them. If the array gets full, the list will allocate a new, larger one. If you know the number of items in advance, you can set the size of the internal array when you construct the list. This way you can avoid unnecessary memory allocation.
You could use an array:
Ticket[] tickets = new Ticket[120];
tickets[45] = ticket
or a Dictionary<int, Ticket>
Dictionary<int, Ticket> tickets = new Dictionary<int, Ticket>();
tickets.Add(45, ticket);
or create a List<Ticket> holding 120 nulls:
List<Ticket> tickets = Enumerable.Repeat(default(Ticket), 120).ToList();
120 is defined as the capacity of the list - not the really existing elements. So in this case your list contains 0 elements at insert.
When you try to insert a element at position 45 into a empty list - a ArgumentOutOfRangeException makes sense
You can use a dictionary also
var dic = new Dictionary<int,Ticket>();
dic[45] = ticket;
You should populate list with something, because this ctor of List doesnt populate it.
public List<Ticket> Tickets = new List<Ticket>(Enumerable.Repeat (new Ticket(), 120));
Tickets[45] = ticket;
The constructor that takes an int is there to define the initial capacity, not the initial number of elements.
When you try to add an element to the list the following code is run:
private void EnsureCapacity(int min)
{
if (this._items.Length >= min)
return;
int num = this._items.Length == 0 ? 4 : this._items.Length * 2;
if ((uint) num > 2146435071U)
num = 2146435071;
if (num < min)
num = min;
this.Capacity = num;
}
The key thing is that it tries to double the capacity each time it runs out of space. This results is an array copy for each doubling. To avoid the computation cost of doubling when you know the size of the list to begin with you set the initial capacity.
You can also create the list through the array, which default initializes all elements:
List<Ticket> tickets = new Ticket[120].ToList();
tickets[45] = ticket;
Dictionary<int, Ticket> Tickets = new Dictionary<int,Ticket>;
Tickets.Add(45, tickets);
Does it need to be a list and does order matter? If not why not use a dictionary or a another key/value object?

List<T> with fixed capacity item removal process time / C#

I have the following question:
List<int> list = new List<int>(10);
for (int i = 0; i < 10; i++)
list.Add(i);
Now list.Count and list.Capacity are 10. It's OK. But what will happen when I will try to remove first item?
list.RemoveAt(0);
Count is now 9 and Capacity still 10, but what happened inside list? List had to go through all the elements like:
list[0] = list[1];
list[1] = list[2];
// etc...
list[9] = null;
?
May be it could be better just to do by myself smthng like:
list[0] = list[list.Count - 1];
? But items order will be changed in this case.
And how long will list.RemoveAt(0) take if I have a List with 10000000 elements with a preinitialized length? Will there be any difference if List will not have preinited length?
UPD:
Looked to the source (didn't know that they are in free access o.O ):
// Removes the element at the given index. The size of the list is
// decreased by one.
//
public void RemoveAt(int index) {
if ((uint)index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
_size--;
if (index < _size) {
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = default(T);
_version++;
}
So it really has Array.Copy inside. What a pity.
Thanks to #TomTom.
What about you go int othe source of List and check and then write some tests? Obviously this is highly important to you. Anyhow, the ton of questions you have all make this quite too broad.
In general, since source are public if often helps to just look into them.
Take a look at the LinkedList. It's only O(1) to remove item from it
As you pointed out for a generic List, a RemoveAt(0) operation will take O(N) for a list of N items. (as it will process N items). This is because a List is backed by an array.
Per MSDN, removing index I from a List with count C takes C - I. You can use this to answer your question around the initial capacity (no it doesnt help)
You can use other data structures, like a LinkedList which is written as a linked list (as the name suggest) and will remove the 1st item in O(1). However, other operations are significantly worse than a List
This is what happens:
public void RemoveAt(int index) {
if ((uint)index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
Contract.EndContractBlock();
_size--;
if (index < _size) {
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = default(T);
_version++;
}
Look it up at:
http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,3d46113cc199059a
Double linked list is the fastest, or use unsafe pointer change.

Insert an underlying value into a non-existing index

I'm trying to solve a simple algorithm a specific way where it takes the current row and adds it to the top most row. I know there are plenty of ways to solve this but currently I have a text file that gets read line by line. Each line is converted to an sbyte (there's a certain reason why I am using sbyte but it's irrelevant to my post and I won't mention it here) and added to a list. From there, the line is reversed and added to another list. Here's the code I have for that first part:
List<List<sbyte>> largeNumbers = new List<List<sbyte>>();
List<string> total = new List<string>();
string bigIntFile = #"C:\Users\Justin\Documents\BigNumbers.txt";
string result;
StreamReader streamReader = new StreamReader(bigIntFile);
while ((result = streamReader.ReadLine()) != null)
{
List<sbyte> largeNumber = new List<sbyte>();
for (int i = 0; i < result.Length; i++)
{
sbyte singleConvertedDigit = Convert.ToSByte(result.Substring(i, 1));
largeNumber.Add(singleConvertedDigit);
}
largeNumber.Reverse();
largeNumbers.Add(largeNumber);
}
From there, I want to use an empty list that stores strings which I will be using later for adding my numbers. However, I want to be able to add numbers to this new list named "total". The numbers I'll be adding to it are not all the same length and because so, I need to check if an index exists at a certain location, if it does I'll be adding the value I'm looking at to the number that resides in that index, if not, I need to create that index and set it's value to 0. In trying to do so, I keep getting an IndexOutOfRange exception (obviously because that index doesn't exist). :
foreach (var largeNumber in largeNumbers)
{
int totalIndex = 0;
foreach (var digit in largeNumber)
{
if (total.Count == 0)
{
total[totalIndex] = digit.ToString(); //Index out of Range exception occurs here
}
else
{
total[totalIndex] = (Convert.ToSByte(total[totalIndex]) + digit).ToString();
}
totalIndex ++;
}
}
I'm just at a loss. Any Ideas on how to check if that index exists; if it does not create it and set it's underlying value equal to 0? This is just a fun exercise for me but I am hitting a brick wall with this lovely index portion. I've tried to use SingleOrDefault as well as ElementAtOrDefault but they don't seem to be working so hot for me. Thanks in advance!
Depending on if your result is have small number of missing elements (i.e. have more than 50% elements missing) consider simply adding 0 to the list till you reach neccessary index. You may use list of nullable items (i.e. List<int?>) instead of regular values (List<int>) if you care if item is missing or not.
Something like (non-compiled...) sample:
// List<long> list; int index; long value
if (index >= list.Count)
{
list.AddRange(Enumerable.Repeat(0, index-list.Count+1);
}
list[index] = value;
If you have significant number of missing elements use Dictionary (or SortedDictionary) with (index, value) pairs.
Dictionary<int, long> items;
if (items.ContainsKey(index))
{
items[key] = value;
}
else
{
items.Add(index, value);
}

Why is AddRange faster than using a foreach loop?

var fillData = new List<int>();
for (var i = 0; i < 100000; i++)
fillData.Add(i);
var stopwatch1 = new Stopwatch();
stopwatch1.Start();
var autoFill = new List<int>();
autoFill.AddRange(fillData);
stopwatch1.Stop();
var stopwatch2 = new Stopwatch();
stopwatch2.Start();
var manualFill = new List<int>();
foreach (var i in fillData)
manualFill.Add(i);
stopwatch2.Stop();
When I take 4 results from stopwach1 and stopwach2, stopwatch1 has always lower value than stopwatch2. That means addrange is always faster than foreach.
Does anyone know why?
Potentially, AddRange can check where the value passed to it implements IList or IList<T>. If it does, it can find out how many values are in the range, and thus how much space it needs to allocate... whereas the foreach loop may need to reallocate several times.
Additionally, even after allocation, List<T> can use IList<T>.CopyTo to perform a bulk copy into the underlying array (for ranges which implement IList<T>, of course.)
I suspect you'll find that if you try your test again but using Enumerable.Range(0, 100000) for fillData instead of a List<T>, the two will take about the same time.
If you are using Add, it is resizing the inner array gradually as needed (doubling), from the default starting size of 10 (IIRC). If you use:
var manualFill = new List<int>(fillData.Count);
I expect it'll change radically (no more resizes / data copy).
From reflector, AddRange does this internally, rather than growing in doubling:
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
int count = is2.Count;
if (count > 0)
{
this.EnsureCapacity(this._size + count);
// ^^^ this the key bit, and prevents slow growth when possible ^^^
Because AddRange checks size of added items and increases size of internal array only once.
The dissassembly from reflector for the List AddRange method has the following code
ICollection<T> is2 = collection as ICollection<T>;
if (is2 != null)
{
int count = is2.Count;
if (count > 0)
{
this.EnsureCapacity(this._size + count);
if (index < this._size)
{
Array.Copy(this._items, index, this._items, index + count, this._size - index);
}
if (this == is2)
{
Array.Copy(this._items, 0, this._items, index, index);
Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index));
}
else
{
T[] array = new T[count];
is2.CopyTo(array, 0);
array.CopyTo(this._items, index);
}
this._size += count;
}
}
As you can see there are some optimizations like EnsureCapacity() call and using Array.Copy().
When using AddRange the Collection can increase the size of the array once and then copy the values into it.
Using a foreach statement the collection needs to increase size of the collection more than once.
Increasing thr size means copying the complete array which takes time.
This is like asking the waiter to bring you one beer ten times and asking him to bring you 10 beers at once.
What do you think is faster :)
i suppose this is the result of optimisation of memory allocation.
for AddRange memory allocates only once, and while foreach on each iteration reallocation is done.
also may be there are some optimisations in AddRange implementation (memcpy for example)
Try out initialize intiial list capacity before manually adding items:
var manualFill = new List<int>(fillData.Count);
It is because the Foreach loop will add all the values that the loop is getting one a time and
the AddRange() method will gather all the values it is getting as a "chunk" and add that chunk at once to the specified location.
Simply understanding, it is just like you have a list of 10 items to bring from the market, which would be faster bringing all that one by one or all at single time.

How to delete a chosen element in array?

I have this assignment where I must delete a chosen element from an array, so I came up with this code:
strInput = Console.ReadLine();
for (int i = 0; i < intAmount; i++)
{
if (strItems[i] == strInput)
{
strItems[i] = null;
for (int x = 0; x < intAmount-i; x++)
{
i = i + 1;
strItems[i - 1] = strItems[i];
}
intAmount = intAmount - 1;
}
}
The problem is that, suppose I have an array [1,2,3,4,5,], and I want to delete 1. The output would be [2,3,4,5,5]. This also happens when I choose 2, but it does not happen when I choose any other number.
What am I doing wrong?
I'm assuming you are working with a basic array of strings:
var strItems = new string[] { "1", "2", "3", "4", "5" };
In .NET, that array is always going to be 5 elements long. In order to remove an element, you are going to have to copy the remaining elements to a new array and return it. Setting the value at a position to null does not remove it from the array.
Now, with things like LINQ this is very easy (not shown here), or you could cheat using the List<> collection and do this:
var list = new List<string>(strItems);
list.Remove("3");
strItems = list.ToArray();
But I don't think that's going to teach you anything.
The first step is to find the index of the element you wish to remove. You can use Array.IndexOf to help you out. Let's find the middle element, "3":
int removeIndex = Array.IndexOf(strItems, "3");
If the element was not found, it will return a -1, so check for that before doing anything.
if (removeIndex >= 0)
{
// continue...
}
Finally you have to copy the elements (except the one at the index we don't want) to a new array. So, altogether, you end up with something like this (commented for explanation):
string strInput = Console.ReadLine();
string[] strItems = new string[] { "1", "2", "3", "4", "5" };
int removeIndex = Array.IndexOf(strItems, strInput);
if (removeIndex >= 0)
{
// declare and define a new array one element shorter than the old array
string[] newStrItems = new string[strItems.Length - 1];
// loop from 0 to the length of the new array, with i being the position
// in the new array, and j being the position in the old array
for (int i = 0, j = 0; i < newStrItems.Length; i++, j++)
{
// if the index equals the one we want to remove, bump
// j up by one to "skip" the value in the original array
if (i == removeIndex)
{
j++;
}
// assign the good element from the original array to the
// new array at the appropriate position
newStrItems[i] = strItems[j];
}
// overwrite the old array with the new one
strItems = newStrItems;
}
And now strItems will be the new array, minus the value specified for removal.
Arrays in C# are of a fixed size - once initialized you can only modify items, but you cannot add or remove items. If you want to delete an item from a collection you have two options:
1.) Create a new array that has all members of the original array minus the one you want to remove.
2.) Use a collection type that is resizable and allows to add or remove items like List<T> (List<int> in your case). This is what you would do in the "real world" if your collection is not static.
In your specific implementation i think u miss a break; statement, you should go out from the outer loop when you finish the inner loop. The assignment to null is not useful at all.
If the list is just a list of numbers why are you using strings? use integers directly if it is the case.
Your exercise seems to ask something like this, if you need to remove only one element.
public bool MyDelete(int[] array, int value) // Easy to do for strings too.
{
bool found = false;
for (int i = 0; i < array.Length; ++i)
{
if (found)
{
array[i - 1] = array[i];
}
else if (array[i] == value)
{
found = true;
}
}
return found;
}
This function will returns true if it find the specified falue, false if not.
It will move all items as you describe in your example, but of course, it will not change the size of the array.
Arrays are fixed size.
You cannot change the size of an array, simply, the language don't allows that.
Arrays are, were and will be always fixed size!
To remove an item from an array you should do something this:
public static T[] RemoveAt<T>(T[] array, int index) // hope there are not bugs, wrote by scratch.
{
int count = array.Length - 1;
T[] result = new T[count];
if (index > 0)
Array.Copy(array, 0, result, 0, index - 1);
if (index < size)
Array.Copy(array, index + 1, result, index, size - index);
return result;
}
...
strItems = RemoveAt(strItems, index);
This function will create a new array that contains all elements except the one at the index you specify.
Now, why someone would do something like this instead of using a List or a Dictionary or wathever?
Use directly a List without using an array.
Can use Except method to filter the data
AllData = {10, 30, 20, 50}
FilterData = {30, 20}
Result = AllData.Except(​FilterData)
Result will be {10, 50}
Arrays are a fixed sized, you can't shorten their length without creating a new array. All you can do is store the length of valid elements in the array (ie. after you remove 1 the length is 4).
Also, I'm not sure if the order of elements in your array is important, but if it's not you could swap the first and last elements rather than moving every element after the one that's removed forward 1 position.
An alternative to using an array is using a collection such as an ArrayList which will take care of resizing, removing and keeping a count of the amount of items in it, plus a lot more.
However, since this is homework you might have to use arrays. Either keep track of the length with a variable, as opposed to using array.length, or create a new array each time you want to change the size. If you don't have to use arrays then look at the collections you can use in C#.

Categories