How does List.RemoveAt(index) work? - c#

Let's say I have a list, messages, with three items. I wan't to loop through them and remove one item at a time.
for (int i = 0; i < messages.Count; i++)
{
messages.RemoveAt(i);
}
(I've removed lots of irrelevant code)
What happen to the remaining messages after the first iteration? Are they moved to another index or can I do it like this to remove all three messages?
Thank you

The index of all elements behind the index you remove will be decremented.
If you want to avoid this with your loop let it run in reverse (delete from highest index to lowest).
for (int i = messages.Count - 1; i >= 0; i--)
{
messages.RemoveAt(i);
}
or just use
messages.Clear()
to delete all elements at once without taking care about any indices.
If you just want to clear the List it's also more efficient to use Clear since it is a O(n) operation. RemoveAt is O(n) as well but inside another O(n) loop which makes it O(n^2) - not that it would matter with 3 elements as mentionend in your example but when talking about larger lists it would certainly make a difference.

In your code, it's simpler to just call messages.Clear();. There's no need to remove each element separately.
Your code will skip every other element as it removes them until the for loop's conditional is no longer met. It will remove the elements at indexes 0 and 2 because you said your collection has three elements.
Let's step through your algorithm:
Initially, the list has three items, listed with their indexes: 0: "Hello", 1: "World", and 2: "Foo".
Your loop removes the element at index 0. The list now looks like this:
0: "World", 1: "Foo"
However, your loop executes again, since i now equals 1 and 1 < 2. The element at index 1 is then removed:
0: "World"
i is incremented to 2 and the conditional is no longer met (i is not less than 1). Your list now consists of what used to be the second element.

You need to iterate backward
for (int i = messages.Count - 1; i >=0; i--)
{
messages.RemoveAt(i);
}
Because in your current loop, you will be left with one time, if your list contains 3 items.
If you want to remove all items from your list then there is a method List<T>.RemoveAll Method

They're moved, see MSDN on List<T>.RemoveAt method:
When you call RemoveAt to remove an item, the remaining items in the
list are renumbered to replace the removed item. For example, if you
remove the item at index 3, the item at index 4 is moved to the 3
position.
To remove all elements, the Clear method is more suitable.

Go thru reverse loop..
for(int i = messages.Count - 1; i >= 0 ; i--) {
messages.RemoveAt(i);
}

You could just changes it to always delete the first one
List<string> messages = new List<string>();
messages.Add("a");
messages.Add("b");
messages.Add("c");
for (int i = 0; i < messages.Count; i++)
{
messages.RemoveAt(0);
}
or to clear whole list in one statement
messages.Clear()

.NET Reference Source has following definition of RemoveAt method:
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++;
}
As you can see - if you remove item which is not last one copying of array items occurs (all items from index + 1 till the end are moved). So in your case its better to remove items from the end to avoid array copying on each iteration:
for (int i = messages.Count - 1; i >= 0; i--)
{
messages.RemoveAt(i);
}
Or simply call messages.Clear() if you want to remove them all without additional logic - in that case internal array just cleared and size set to zero.

Like the other posts, you need to iterate backward
You've got many solutions to remove the items
messages.Clear();
or
while(messages.Count != 0){
message.RemoveAt(0);
}

Related

How can I check for integers in list and print the index of them?

so my problem is that I don't know how to go forward in the list and print the next same integer if there is one.
Here is what I have at the moment:
while (list.Contains(input1))
{
Console.WriteLine(input1 + " is at index " + list.IndexOf(input1))
}
I am trying to list all of the integers that are in the list and print the index of them. But not remove after finding one of the integers (this was at least my first idea.).
IndexOf has an overload with two parameters, which allows you to start searching at a later position in the list.
Since this is obviously a learning exercise, I won't spoil it by providing the full code, but rather suggest that you try to implement the following algorithm:
Find the index of input starting at position 0.
If not found (i.e., IndexOf returns -1): we're done. Otherwise:
Print and remember that index.
Start again at step 1, but this time, don't start searching at 0 but at the index you remembered + 1.
You can do the following:
go through the list/array using for statement
for(int i=0; i < list.length; i++) // loop though list
then inside the loop check the value of the current item using if statement:
if(list[i] == input1)
//do smothing
The list[0] represent the first item in the array, which means the index is 0.
so in the example above the i will be the current index so long that you in the loop.
I didn't write the full code for learning purpose in reference to #Heinzi answer.
Hope that could be helpful!
This is an implementation possibility. It is longer than it has to be, but it makes it clearer for beginners how one could tackle this problem.
Since you wanted to only show numbers that come up more than once here is an implementation method. If you want to show numbers that come up only once too just erase everything about lastindex
List<int> yourlist = new List<int> { 1,1,1,1,1,11,2,3,3,4,4,5 };
int input = 0;
input = Convert.ToInt32(Console.ReadLine());
var index = yourlist.IndexOf(input);
//this checks if your input is in the list
var lastindex = yourlist.LastIndexOf(input);
//this does the same but it searches for the last implementation of your input
if (index != -1 && lastindex != index)
//this if checks if your number comes up more than once. IndexOf returns -1 if there is no occurence of your input
{
Console.Write($"the index of {input} is {index}");
for (int i = index+1; i <= yourlist.Count; i++)
//this loop takes the position of the first occurence of your number and then counts up from there
{
var tempindex = yourlist.IndexOf(input, i);
if (tempindex != -1)
//this if lets everything except -1 through
{
Console.Write($" and {tempindex}");
}
}
}
else
{
Console.WriteLine("your number cannot be found twice in the list");
}

Pre-decrement of value in a for loop is not correctly decreasing value on first loop

I have the following for loop in my code (using C#)
for (int i = 150; i >= 75; --i)
{
print("I holds " + i);
images[i].gameObject.SetActive(false);
}
trying to run through each item in a list, and disable the object. The list holds 150 objects (but since it starts at value zero, the final reference position is 149)
So, I figured a for loop was a good way to iterate through them all. Yet, I try to decrease the value of i to 149 for the first run of the loop, but it still passes a value of 150 into the loop in the first run, which throws the expected ("Argument is out of range") error.
Can anyone work out why the decreased value isn't correctly being passed to the loop?
I tried both decreasing it before and after the first run of the loop, but both times it passes a value of 150 into the loop.
I feel this should be a relatively simple issue to solve, yet it's not working as I expected it to do!
for (int i = 10; i >= 0; --i)
is the same as
for (int i = 10; i >= 0; i--)
i does not decrease/increase on the first loop. This is for many languages. Just start with 149 and it works.
Answer for "Can anyone work out why the decreased value isn't correctly being passed to the loop?"
Another way to loop through all items of an array without caring of actual indices is to make use of a foreach statement:
foreach(var image in images)
{
image.gameObject.SetActive(false);
}
If you want to use a for statement. I would suggest you write it as below:
for(var i=0; i<images.Length; i++)
{
image[i].gameObject.SetActive(false);
}
Doing so, you are pretty confident that you are not going to be out of the array's size. You start at the element at the position with index of 0 and you read the last item stored in the array, in the position of images.Length-1.
Update
If you want to update only the first 75 items (where 75 is half the total items in the array) in your array you could try this:
for(var i=0; i<images.Length/2; i++)
{
image[i].gameObject.SetActive(false);
}

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.

Looping and changing a list - remove doesn't always work

I'm trying to go through a loop 40 times and changing a list in the process.
This is the code:
for (int i = 0; i < 40; i++)
{
location = rand.Next(rows.Count);
rank = rand2.Next(pondRanks.Count);
ComputerPonds[rows[location]].Rank = (PondRank)pondRanks[rank];
rows.Remove(location);
pondRanks.Remove(rank);
}
For some reason the remove doesn't happen all the time, and only sometimes. Anyone has a suggestion?
Both of the list are List , they have 40 elements, and I want to remove the element itself.
Even when debugging I can see that the list count isn't the same (they both have the same initial numbers and they both need to do remove at this loop). If it matters, I'm working on windows phone platform..
I'm pretty sure you should be using List.RemoveAt not List.Remove. RemoveAt will remove the item at the specified index, whereas Remove will look for that object you passed in and remove it from the List if it's in there. But I'm pretty sure that looking at your code that location and rank represent the index, not the objects themselves.
for (int i = 0; i < 39; i++)
{
location = rand.Next(rows.Count);
rank = rand2.Next(pondRanks.Count);
ComputerPonds[location].Rank = (PondRank)pondRanks[rank];
rows.RemoveAt(location);
pondRanks.RemoveAt(rank);
}
EDIT: You may also want to consider making sure that your rows and pondRanks have enough elements (39) before starting the loop (or altering the i < 39 to max out at the upper limit of their length)

Clearing a list using a for loop

I'm making a Black Jack game, and at the start of every new round I need to clear the list of cards that represents the Player's and the Dealer's hands. I used this to do so:
public void ClearPlayerHand()
{
for (int i = 0; i < PlayerHand.Count; ++i)
{
PlayerHand.Remove(PlayerHand[i]);
}
}
Problem is I always seem to be left with one card left in the list, or I receive an out of bounds error, no matter how I change the value of i, what is the best method of removing all the elements from the PlayerHand?
If your collection PlayerHand implements ICollection<T> you can just call the .Clear() method.
A common implementation of this interface is List<T>.
If you do want to clear a List<T> via a for loop, you should use a reverse for loop. The reason for this is that as you remove an item from the list, it will shift all the index's down one, and you could easy run into index out of bounds exceptions.
An example of this would be:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
The other answers are right: use Clear.
But, if you wanted to do this with a loop and Remove calls, here's how you would do it:
for(int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
Reversing the direction of the iteration is the real trick.
This is the best/easiest way to do it.
PlayerHand.Clear();
Reason for out of bounds
As for why you are receiving the out of bounds exception, it's happening because you're removing elements from the list but continually counting up. You would want the last operation to remove i = 0 but it keeps counting.
Say PlayerHand has 3 items in it, the following occurs:
i = 0
remove PlayerHand[0] (it now contains 2 elements)
i = 1
remove PlayerHand[1] (it now contains 1 element)
i = 2
remove PlayerHand[2] (this throws an exception as only PlayerHand[0] exists)
Normally you would count backwards in this case:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
Alternatively, you can consider using data binding and then you should update the ItemSource, instead of directly manipulating the listbox or listview items.
List<T> SomeSource=...
PlayHand.ItemSource=SomeSource;
SomeSource.Clear();
Another suggested approach beside Clear method, you can also use RemoveAll to either remove all or part of list
// Remove all items
PlayerHand.RemoveAll(x => true);
// Remove part of list
PlayerHand.RemoveAll(x => ConditionMethod(x));

Categories