Right now I am reading a few posts about List<T> vs LinkedList<T> , so I decided to evaluate some structures myself. I compared Stack<T> , Queue<T> , List<T> and LinkedList<T> , adding data and removing data to / from the front / end. Here is the test result:
Pushing to Stack... Time used: 7067 ticks Poping from Stack... Time used: 2508 ticks Enqueue to Queue... Time used: 7509 ticks Dequeue from Queue... Time used: 2973 ticks Insert to List at the front... Time used: 5211897 ticks RemoveAt from List at the front... Time used: 5198380 ticks Add to List at the end... Time used: 5691 ticks RemoveAt from List at the end... Time used: 3484 ticks AddFirst to LinkedList... Time used: 14057 ticks RemoveFirst from LinkedList... Time used: 5132 ticks AddLast to LinkedList... Time used: 9294 ticks RemoveLast from LinkedList... Time used: 4414 ticks
The code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace Benchmarking { static class Collections { public static void run() { Random rand = new Random(); Stopwatch sw = new Stopwatch(); Stack<int> stack = new Stack<int>(); Queue<int> queue = new Queue<int>(); List<int> list1 = new List<int>(); List<int> list2 = new List<int>(); LinkedList<int> linkedlist1 = new LinkedList<int>(); LinkedList<int> linkedlist2 = new LinkedList<int>(); int dummy; sw.Reset(); Console.Write("{0,40}", "Pushing to Stack..."); sw.Start(); for (int i = 0; i < 100000; i++) { stack.Push(rand.Next()); } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "Poping from Stack..."); sw.Start(); for (int i = 0; i < 100000; i++) { dummy = stack.Pop(); dummy++; } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks\n", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "Enqueue to Queue..."); sw.Start(); for (int i = 0; i < 100000; i++) { queue.Enqueue(rand.Next()); } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "Dequeue from Queue..."); sw.Start(); for (int i = 0; i < 100000; i++) { dummy = queue.Dequeue(); dummy++; } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks\n", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "Insert to List at the front..."); sw.Start(); for (int i = 0; i < 100000; i++) { list1.Insert(0, rand.Next()); } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "RemoveAt from List at the front..."); sw.Start(); for (int i = 0; i < 100000; i++) { dummy = list1[0]; list1.RemoveAt(0); dummy++; } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks\n", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "Add to List at the end..."); sw.Start(); for (int i = 0; i < 100000; i++) { list2.Add(rand.Next()); } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "RemoveAt from List at the end..."); sw.Start(); for (int i = 0; i < 100000; i++) { dummy = list2[list2.Count - 1]; list2.RemoveAt(list2.Count - 1); dummy++; } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks\n", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "AddFirst to LinkedList..."); sw.Start(); for (int i = 0; i < 100000; i++) { linkedlist1.AddFirst(rand.Next()); } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "RemoveFirst from LinkedList..."); sw.Start(); for (int i = 0; i < 100000; i++) { dummy = linkedlist1.First.Value; linkedlist1.RemoveFirst(); dummy++; } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks\n", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "AddLast to LinkedList..."); sw.Start(); for (int i = 0; i < 100000; i++) { linkedlist2.AddLast(rand.Next()); } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks", sw.ElapsedTicks); sw.Reset(); Console.Write("{0,40}", "RemoveLast from LinkedList..."); sw.Start(); for (int i = 0; i < 100000; i++) { dummy = linkedlist2.Last.Value; linkedlist2.RemoveLast(); dummy++; } sw.Stop(); Console.WriteLine(" Time used: {0,9} ticks\n", sw.ElapsedTicks); } } }
The differences are so dramatic!
As you can see, the performance of Stack<T> and Queue<T> is fast and comparable, as expected.
For List<T> , the use of front and end has so many differences! And, to my surprise, the performance of adding / removing from the end is actually comparable to the performance of Stack<T> .
For LinkedList<T> , edge manipulation is fast (-er than List<T> ) , but for the end it's incredibly slow to delete when processing ends too.
So ... can any experts consider:
- similarities in performance using
Stack<T> and the end of List<T> , - differences in the use of the front and end of
List<T> and - Is the reason that the use of the end of
LinkedList<T> so slow (not applicable as it is a coding error due to the use of Linq Last() , thanks to CodesInChaos )?
I think I know why List<T> doesn’t do so well with the front ... because List<T> has to move the whole list back and forth when this is done. Correct me if I am wrong.
PS My System.Diagnostics.Stopwatch.Frequency is 2435947 , and the program is for the .NET client profile and compiled with C # 4.0 in Windows 7 Visual Studio 2010.