3/27/2017
In this tutorial, I’m going to explain the wonders of using a Generic List instead of an array. Please keep in mind that there are times when using an array is better suited for what you need, though I generally tend to use Generic Lists more often than not. First off, what is a Generic List? A Generic List is basically a one-dimensional array with more features and functionality. Let’s take a look first at declaring an array versus declaring a Generic List. or Do you see the differences there? When we declared the array, we either had to declare it’s element list right away, or specify how many values it will hold. With the Generic List, we didn’t do either one, that’s because you can add values to the list after it’s declared. You can also add values to a generic list when declaring it Notice how you declared variables for the generic list, the same way you did for an array at declaration. Now, let’s say we’ve got that Generic list declared and populated with values, and we wanted to remove the number 3 from it. In an array, we’d have to create a new array with a length of the first array minus one, then copy each value into the new array, excluding the one we wanted to remove. However, in a Generic List, it’s as simple as this: By this point, your probably wondering if there is anything special you have to do to use a Generic List. To use a Generic List in your code, you must include a using statement like this: That’s it, you now know the basics behind using a Generic List instead of an array. With the primer behind us, perhaps we should move on to more advanced topics. Often, in game development you’ll find yourself creating a two-dimensional array to hold X,Y values for object locations. This can be done easily with Generic Lists, and can actually make your code a little bit easier to read. To do this, you’ll first need to create a Coordinate class: With the class created, you can then use a generic list to track the coordinates: That’s right, we just made a list of the Coordinates class, and added two coordinates to it. But what if we wanted to add those two coordinates while we were creating the list? Both of these will result in the Now, let’s get loopy. When working with lists, its very tempting to use the following loop to iterate through each element in the list: While this works like a charm in most cases, there is one special case to take note of. If for any reason you are going to remove or add elements t6/from the List while iterating over the list, you can not use a ForEach to loop through the list. Doing so will cause run-time errors. It’ll usually crash saying that the Collection was modified. Here’s the right way to iterate over a List if you’re removing or adding elements: Remember I said earlier that a Generic List is a one-dimensional array with extra features and functionality, that means we can iterate over it the same way we do an array. But why are we going backwards from the end of the list? Well, the if statement in the above code is checking to see if the index is an even number, if it is then we remove this coordinate. Because we are adding or removing items from the list, we process the list in reverse order to prevent potential errors. Notice also the way we specified which element was to be removed in the list? When using the Remove function of a list, it expect to be passed a copy of the element to be removed. Since we’re iterating over the list as an array, we aren’t grabbing a reference to the element, so we had to specify which element to remove using it’s index in the list, which is what i is holding. In a ForEach loop, we’re grabbing a reference to each element in the loop, so we don’t know what it’s index is. As part of good coding practice, even when using a Generic List, it’s better to use a For loop instead of a ForEach loop, not saying it’s bad to use a ForEach loop. If in the loop you call any method outside of the loop, you are taking a chance that the called method will remove an element from the list, which as stated before will cause the “collection was modified” error I spoke of earlier. What if you wanted to iterate over Coords, adding 1 to X and removing any elements whose X value was over 20? Here’s how you’d do that: Likewise, if you weren’t going to remove any elements, but just wanted to add one to Y, then you could use a ForEach like this: Whoah…what’s up with that c? Well, in each iteration of the ForEach loop, c is made to be a reference to the element being evaluated. This means, that any changes to make to c on the first iteration, are actually being made to the first element in the List. Changes to c in the second iteration are being made to the second element in the List, and so on. Before I leave, there’s one more thing to take note of, and this is very important. You would be wrong, and you’d find out pretty quickly when you start modifying the X and Y values for the bullets. What you would see on the screen would be totally different than what you are expecting. When you go to move all the bullets up 10 points, you’ll actually see all the bullets and the enemies all move up 10 points. Why is this? When you make a copy of a list like we did above, your actually making a reference list to the original list. This means the Bullets list, does not actually contain the coordinates, but pointers back to the Coords list. Here’s one correct way to make a separate, non referential copy of a list(only works with some compilers): The two lines above make an actual separate copy of the Coords list. Now when you move the bullets up 10 points, only the bullets will move and the enemies will stay in place. The above code may still move all enemies and bullets on some compilers, that’s because not all compilers are built the same. If you use the above code and experience this issue, then that’s because each instance of the Coordinate class stored in Coords is a pointer. To fix this with those compilers, you’d have to rewrite the above code to the following: That’s right, on those compilers(who implement classes and lists properly), you actually have to add new instances of the Coordinates class for every element in Coords that you want to duplicate. It should be noted, that while the first method may work on some compilers, the second method works on all compilers, and therefore should always be the one used.int[] iVal = {1,2,3,4};
int[4] iVal;
List<int> lVal = newList<int>();
lVal.Add(1);
lVal.Add(2);
lVal.Add(3);
lVal.Add(4);
List<int> lVal = newList<int>{1, 2, 3, 4};
lVal.Remove(3);
using System.Collections.Generic;
class Coordinates
{
public float X;
public float Y;
public Coordinates(float fX, floatfY)
{
X = fX;
Y = fY;
}
}
List<Coordinates> Coords = new List<Coordinates>();
Coords.Add(new Coordinates(1.5, 2.6));
Coords.Add(new Coordinates(3, 4.7));
List<Coordinates> Coords = new List<Coordinates>
{
newCoordinates(1.5, 2.6),
newCoordinates(3,4.7)
};
foreach(Coordinates c in Coords)
{
PerformSomeAction();
}
for(int i = Coords.Length; i > 0; i--)
{
PerformSomeAction();
if(i % 2 == 0)
Coords.RemoveAt(i);
}
for(int i = Coords.Length(); i > 0; i--)
{
x++;
if(Coords[i].X > 20)
{
Coords.Remove(Coords[i]);
}
}
foreach(Coordinates c inCoods)
{
c.Y++;
}
Let’s say you’ve got the Coords list we’ve been working with above, and it provides coordinates for all enemies in your game. Then all your enemies fire at once, and you want to create a list of coordinates for the bullets. You might think that you can create this second list this way:List<Coordinates> Bullets = Coords;
List<Coordinates> Bullets = new List<Coordinates>();
foreach(Coordinates cor inCoords) Bullets.Add(cor);
List<Coordinates> Bullets = newList<Coordinates>();
foreach(Coordinates cor inCoords)
{
Bullets.Add(newCoordinates(cor.X, cor.Y));
}