A graduate developer asked a simple question the other day.

Grad : “What type should I return from this method?”

Me : “Make it a list”

Seemed simple enough. I took a look at his code and was confounded on just what had gone wrong. Instead of using List<T>, they had used the type “ArrayList”. I honestly can’t even remember the last time I used ArrayList. I think maybe right when I started programming in .NET 2, I couldn’t understand generics quick enough and the ArrayList seemed like a drop in replacement. It’s not!

What’s The Difference?

The key difference between the two is that an ArrayList holds only types of “objects”. That means theoretically it’s a box of anything you want it to be. For example this code compiles just fine :

ArrayList arrayList = new ArrayList(); arrayList.Add(123); arrayList.Add("abc"); arrayList.Add(new object());

It’s then on the code grabbing things out of the array list to “check” that it’s the correct type. In practice it’s not going to be so haphazard that you are throwing all sorts of types in an array list so really it’s more of a compile time “looseness”. If we compare it to a List :

List<int> list = new List<int>(); list.Add(123); list.Add("abc"); //Compile time error

No bueno. It knows that we only want to be storing integers and trying to jam anything else in there isn’t going to fly.

But what about this?

List<object> list = new List<object>(); list.Add(123); list.Add("abc");

That works right? A list of objects is almost the same thing as an ArrayList. Almost.

If we look at the interfaces implemented by ArrayList :

public class ArrayList : ICollection, IEnumerable, IList, ICloneable

List is basically the same with a few generic interfaces thrown in. However when you check these, they don’t add anything except generic methods of things like “Add” etc that use the type :

public class List<T> : ICollection<T>, IEnumerable<T>, IEnumerable, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection, IList

But where things change is using LINQ. Almost all methods (I say almost but I think it’s all) are built upon IEnumerable<T> and not IEnumerable. For example the Where clause in LINQ looks like :

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

That means you cannot use LINQ on an ArrayList. In some use cases it’s no biggie, but you do get LINQ for free… So picking a type that doesn’t support it is really shooting yourself in the foot for no reason.

Performance Implications

In some cases, there are large performance implications when picking an ArrayList over a List<T>. That comes down to the act of “boxing” and “unboxing”. In simple terms, boxing is taking a value type (such as an integer) and wrapping it in an object and storing it on the heap instead of the stack. Microsoft actually have a great article on the subject here : https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing

But how does that affect the List vs ArrayList conversation? When we store an item in an ArrayList it must be of type object (Or a type of). If we are storing a value type in our ArrayList, then before it can be stored, it must first “box” the object and wrap it. A List<int> does not have the same boxing cost (Although a List<object> would).

To test this, I created a benchmark using BenchmarkDotNet

public class ArrayListVsListWrite { int itemCount = 10000000; public ArrayList arrayList; public List<int> list; public List<object> listObject; [IterationSetup] public void Setup() { arrayList = new ArrayList(); list = new List<int>(); listObject = new List<object>(); } [Benchmark] public ArrayList WriteArrayList() { for(int i=0; i < itemCount; i++) { arrayList.Add(i); } return arrayList; } [Benchmark] public List<object> WriteListObject() { for (int i = 0; i < itemCount; i++) { listObject.Add(i); } return listObject; } [Benchmark] public List<int> WriteList() { for (int i = 0; i < itemCount; i++) { list.Add(i); } return list; } }

In simple terms. We are looping 10 million times and adding the item to the list. The results of which are :

Method Mean Error StdDev WriteArrayList 651.48 ms 4.215 ms 3.943 ms WriteListObject 641.95 ms 5.129 ms 4.798 ms WriteList 88.49 ms 5.631 ms 16.603 ms

Not hard to see the performance difference here. We can see that a List of type object also suffers the same boxing/unboxing problem.

To a lesser extent, reading would also be slower as you would be reading an object, and then “unboxing” that object and casting it to an integer. I was going to do a benchmark with that but… you get the idea.

When Should You Use ArrayList?

Honestly. Never.

The only time you should use an ArrayList is when you are using a library built before List<T> which I believe was introduced in .NET 2.0. If you are using a library (Or building code) that targets .NET 1.0 or 1.1, then I guess ArrayList is OK. It’s probably going to be the least of your problems.