What is the difference between ArrayList
and List<>
in C#?
Is it only that List<>
has a type while ArrayLis
Performance has already been mentioned in several answers as a differentiating factor, but to address the “How much slower is the ArrayList
?” and “Why is it slower overall ?”, have a look below.
Whenever value types are used as elements, performance drops dramatically with ArrayList
. Consider the case of simply adding elements. Due to the boxing going on - as ArrayList
’s Add only takes object
parameters - the Garbage Collector gets triggered into performing a lot more work than with List<T>
.
How much is the time difference ? At least several times slower than with List<T>
. Just take a look at what happens with code adding 10 mil int values to an ArrayList
vs List<T>
:
That’s a run time difference of 5x in the ‘Mean’ column, highlighted in yellow. Note also the difference in the number of garbage collections done for each, highlighted in red (no of GCs / 1000 runs).
Using a profiler to see what’s going on quickly shows that most of the time is spent doing GCs, as opposed to actually adding elements. The brown bars below represent blocking Garbage Collector activity:
I’ve written a detailed analysis of what goes on with the above ArrayList
scenario here https://mihai-albert.com/2019/12/15/boxing-performance-in-c-analysis-and-benchmark/.
Similar findings are in “CLR via C#” by Jeffrey Richter. From chapter 12 (Generics):
[…] When I compile and run a release build (with optimizations turned on) of this program on my computer, I get the following output.
00:00:01.6246959 (GCs= 6) List<Int32>
00:00:10.8555008 (GCs=390) ArrayList of Int32
00:00:02.5427847 (GCs= 4) List<String>
00:00:02.7944831 (GCs= 7) ArrayList of StringThe output here shows that using the generic List algorithm with the Int32 type is much faster than using the non-generic ArrayList algorithm with Int32. In fact, the difference is phenomenal: 1.6 seconds versus almost 11 seconds. That’s ~7 times faster! In addition, using a value type (Int32) with ArrayList causes a lot of boxing operations to occur, which results in 390 garbage collections. Meanwhile, the List algorithm required 6 garbage collections.
Using "List" you can prevent casting errors. It is very useful to avoid a runtime casting error.
Example:
Here (using ArrayList) you can compile this code but you will see an execution error later.
// Create a new ArrayList
System.Collections.ArrayList mixedList = new System.Collections.ArrayList();
// Add some numbers to the list
mixedList.Add(7);
mixedList.Add(21);
// Add some strings to the list
mixedList.Add("Hello");
mixedList.Add("This is going to be a problem");
System.Collections.ArrayList intList = new System.Collections.ArrayList();
System.Collections.ArrayList strList = new System.Collections.ArrayList();
foreach (object obj in mixedList)
{
if (obj.GetType().Equals(typeof(int)))
{
intList.Add(obj);
}
else if (obj.GetType().Equals(typeof(string)))
{
strList.Add(obj);
}
else
{
// error.
}
}
Another difference to add is with respect to Thread Synchronization.
ArrayList
provides some thread-safety through the Synchronized property, which returns a thread-safe wrapper around the collection. The wrapper works by locking the entire collection on every add or remove operation. Therefore, each thread that is attempting to access the collection must wait for its turn to take the one lock. This is not scalable and can cause significant performance degradation for large collections.
List<T>
does not provide any thread synchronization; user code must provide all synchronization when items are added or removed on multiple threads concurrently.
More info here Thread Synchronization in the .Net Framework
Using List<T>
you can prevent casting errors. It is very useful to avoid a runtime casting error.
Example:
Here (using ArrayList
) you can compile this code but you will see an execution error later.
ArrayList array1 = new ArrayList();
array1.Add(1);
array1.Add("Pony"); //No error at compile process
int total = 0;
foreach (int num in array1)
{
total += num; //-->Runtime Error
}
If you use List
, you avoid these errors:
List<int> list1 = new List<int>();
list1.Add(1);
//list1.Add("Pony"); //<-- Error at compile process
int total = 0;
foreach (int num in list1 )
{
total += num;
}
Reference: MSDN
To add to the above points. Using ArrayList
in 64bit operating system takes 2x memory than using in the 32bit operating system. Meanwhile, generic list List<T>
will use much low memory than the ArrayList
.
for example if we use a ArrayList
of 19MB in 32-bit it would take 39MB in the 64-bit. But if you have a generic list List<int>
of 8MB in 32-bit it would take only 8.1MB in 64-bit, which is a whooping 481% difference when compared to ArrayList.
Source: ArrayList’s vs. generic List for primitive types and 64-bits
To me its all about knowing your data. If I am continuing to expand my code on the basis of efficiency, I would have to choose the List option as a way of deciphering of my data w/o the unnecessary step of always wondering about types, especially 'Custom Types'. If the machine understands the difference and can determine on it's on what type of data I'm actually dealing with then why should I get in the way and waste time going thru the gyrations of 'IF THEN ELSE' determinations? My philosophy is to let the machine work for me instead of me working on the machine? Knowing the unique differences of different object code commands goes a long way in making your code as efficient.
Tom Johnson (One Entry ... One Exit)