should i lock local variable in multi-threading? - c#

I am testing simple caching logic in c#. Here is my CacheManager class:
public class CacheManager
{
static List<string> _list = new List<string>();
static readonly object _syncobject = new object();
public static void ReloadList()
{
List<string> list = new List<string>();
Random r = new Random();
var count = r.Next(10);
for (int i = 0; i < count; i++)
{
list.Add("list" + i);
}
//lock (_syncobject)
{
_list = list;
}
}
public static IEnumerable<string> GetList()
{
//lock (_syncobject)
{
return _list;
}
}
}
Below is the class which spawns many threads consuming CacheManager:
class Program
{
static void Main(string[] args)
{
//threads for re-loading the list
for (int i = 0; i < 3; i++)
{
Thread reloadThread = new Thread(ReloadList);
reloadThread.Start();
}
//threads for getting the list
for (int i = 0; i < 10; i++)
{
Thread tget = new Thread(PrintList);
tget.Start();
}
//threads for getting the list and storing in local variable then use it
for (int i = 0; i < 10; i++)
{
Thread tget = new Thread(PrintListWithLocalVariable);
tget.Start();
}
Console.ReadKey();
}
private static void ReloadList()
{
do
{
Console.WriteLine("Reloading **********");
CacheManager.ReloadList();
} while (true);
}
private static void PrintList()
{
do
{
foreach (var item in CacheManager.GetList())
{
if (item == null)
throw new Exception("i == null");
Console.WriteLine(item);
}
} while (true);
}
private static void PrintListWithLocalVariable()
{
do
{
var list = CacheManager.GetList();
foreach (var listitem in list)
{
var i = list.FirstOrDefault(x => x.Equals(listitem));
if (i == null)
throw new Exception("i == null");
Console.WriteLine("Printing with Local variable:" + listitem);
}
} while (true);
}
}
My understanding was we should lock the _list variable in CacheManager, but doesn't look like we need that. I ran the above test for an hour or so but didn't get any error. While ReloadThread is reloading the random number of list items, other threads which are looping through the list, i thought might have issues. Can anybody explain me why the program is running without issues?
Thanks.

_list is a static variable, meaning that every instance of CacheManager will share the same instance of _list. Access to _list should indeed be locked to prevent concurrency issues. As someone mentioned in a comment, a ConcurrentCollection should be used instead of List(of T) as well, as List(of T) is not thread-safe.

Related

Fill TreeView with data from a collection asynchronously

I am trying to add a batch of nodes to a TreeView asynchronously, in 50ms time intervals, but I am getting an exception that says the collection has been modified. How can I prevent this exception from occurring?
Collection was modified; enumeration operation may not execute.
public partial class Form1 : Form
{
private SynchronizationContext synchronizationContext;
private DateTime previousTime = DateTime.Now;
private List<int> _batch = new List<int>();
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
synchronizationContext = SynchronizationContext.Current;
await Task.Run(() =>
{
for (var i = 0; i <= 5000000; i++)
{
_batch.Add(i);
UpdateUI(i);
}
});
}
public void UpdateUI(int value)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 50) return;
synchronizationContext.Post(new SendOrPostCallback(o =>
{
foreach (var item in _batch)
{
TreeNode newNode = new TreeNode($"{item}");
treeView1.Nodes.Add(newNode);
}
}), null);
_batch.Clear();
previousTime = timeNow;
}
}
List<T> is not thread safe. You are trying to modify the list while you are trying to read from it in your foreach.
There are a few ways to solve this.
Use ConcurrentQueue
Change:
private List<int> _batch = new List<int>();
To:
var _batch = new ConcurrentQueue<int>();
You can add items by calling:
_batch.Enqueue(1);
Then you can grab them all out and add them in a thread-safe manner:
while(_batch.TryDequeue(out var n))
{
// ...
}
Use List, but with Thread-synchronization Objects
Create a new object like so:
private readonly _listLock = new object();
Then add these helper methods:
private void AddToBatch(int value)
{
lock(_listLock)
{
_batch.Add(value);
}
}
private int[] GetAllItemsAndClear()
{
lock(_listLock)
{
try
{
return _batch.ToArray();
}
finally
{
_batch.Clear();
}
}
}
This ensures only one thread at a time is operating on the List object.
There are other ways to do this as well, but this is the gist of the problem. Your code won't be as fast because of the overhead of synchronizing data, but you won't crash.

c# Threading Lock and Monitor

I'm currently running into a problem with multithreading and accessing a static list. A static list holds all items with several properties. The items are identified with a Guid. A main work thread changes some properties for any item in the static list. The child threads all have their own Guid, with this Guid they read their own item in the static list. And after a specific event they remove their assigned element from the static list.
To get to the source I have broken down my code to the essential methods and classes. The work thread has the following simplified code
public void RunWork()
{
Random random = new Random();
Int32 index = -1;
while (!Kill)
{
Thread.Sleep(1);
if (MainWindow.Clients != null)
{
index = random.Next(0, MainWindow.Clients.Count);
MainWindow.Clients[index].State = MainWindow.RandomString(9);
}
}
}
Each child thread has the following simplified code
public void RunChild()
{
Random random = new Random();
while (!Kill)
{
Thread.Sleep(100);
if (MainWindow.Clients.Any(x => x.Id == Id))
{
this.State = MainWindow.Clients.First(x => x.Id == Id).State;
}
Thread.Sleep(random.Next(50));
if (random.Next(100) % 90 == 0)
{
Kill = true;
MainWindow.Clients.RemoveAll(x => x.Id == Id);
}
}
}
If a child removes itself from the MainWindow.Clients list the work thread throws a exception, that the index it is trying to access does not exist.
I have added lock statments around every access of MainWindow.Clients but this does not prevent the work thread from accessing a deleted item. I have also tried Monitor.Enter(MainWindow.Clients) and Monitor.Exit(MainWindow.Clients) but with the same result as with lock.
The static list MainWindow.Clients is created before any thread runs and never gets recreated or disposed.
If the lock statement is set around this block of code in the RunWork() method
lock (MainWindow.Clients)
{
Thread.Sleep(1);
if (MainWindow.Clients != null)
{
index = random.Next(0, MainWindow.Clients.Count);
MainWindow.Clients[index].State = MainWindow.RandomString(9);
}
}
Why does it not block the child threads from changing the list between the lines
where the random index is set and the list gets accessed?
Update 1:
The following code still throws a IndexOutOfRangeException at MainWindow.Clients[index].State = MainWindow.RandomString(9);:
public void RunWork()
{
Random random = new Random();
Int32 index = -1;
while (!Kill)
{
Thread.Sleep(1);
if (MainWindow.Clients != null)
{
lock (MainWindow.Clients)
{
index = random.Next(0, MainWindow.Clients.Count);
MainWindow.Clients[index].State = MainWindow.RandomString(9);
}
}
}
}
public void RunChild()
{
Random random = new Random();
while (!Kill)
{
Thread.Sleep(100);
if (MainWindow.Clients.Any(x => x.Id == Id))
{
this.State = MainWindow.Clients.First(x => x.Id == Id).State;
}
Thread.Sleep(random.Next(50));
if (random.Next(100) % 90 == 0)
{
Kill = true;
lock (MainWindow.Clients)
{
MainWindow.Clients.RemoveAll(x => x.Id == Id);
}
}
}
}
Update 2: Here is the complete code for the quick sample application
Update 3: I have edited my code and wrapped all accesses of MainWindow.Clients with lock statements. But still the threads access the variable while it is locked:
I'm not sure what exactly you are trying to achieve, but I've written something that might help you find the correct solution. Sorry for the lack of correctness - tight schedule ;-)
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
namespace ConcurrentCollectionTest
{
internal class Client
{
public string State
{
get; set;
}
public string Name
{
get;
internal set;
}
}
internal class MainWindow
{
private ConcurrentDictionary<int, Client> _dict = new ConcurrentDictionary<int, Client>();
public IDictionary<int, Client> Clients
{
get
{
return _dict;
}
}
}
internal class Program
{
private static bool killAll = false;
private static MainWindow mainWindow = new MainWindow();
private static int id = -100;
private static string state = "Initial";
private static Random random = new Random();
private static object lockObject = new object();
internal static string RandomString(int v)
{
int k = random.Next(0, v);
return k.ToString();
}
public static void RunChild()
{
Debug.WriteLine($"child running {Thread.CurrentThread.Name}");
bool killThis = false;
while (!killThis && !killAll)
{
Thread.Sleep(100);
Client client = null;
if (mainWindow.Clients.TryGetValue(id, out client))
{
state = client.State;
}
Thread.Sleep(random.Next(50));
if (random.Next(100) % 90 == 0)
{
Debug.WriteLine($"killing {Thread.CurrentThread.Name}");
killThis = true;
lock (lockObject)
{
mainWindow.Clients.Remove(id);
}
}
}
}
public static void RunWork()
{
Console.WriteLine("RunWork");
Random random = new Random();
Int32 index = -1;
while (!killAll)
{
if (!mainWindow.Clients.Any())
{
killAll = true;
break;
}
Thread.Sleep(100);
// edit: still need lock here as count can change in between
Client client = null;
lock (lockObject)
{
index = random.Next(0, mainWindow.Clients.Count);
client = mainWindow.Clients[index];
}
Debug.WriteLine($"Changing {client.Name}");
client.State = RandomString(9);
}
Console.WriteLine("Worker killed");
}
private static void Main(string[] args)
{
Console.WriteLine("Starting. Enter id or kill");
for (int i = 0; i < 100; i++)
{
mainWindow.Clients.Add(i, new Client
{
Name = $"Client {i:000}",
State = "Unknown"
});
}
var worker = new Thread(RunWork);
worker.Start();
var threadList = new List<Thread>();
threadList.Add(worker);
for (int i = 0; i < 10; i++)
{
var thread = new Thread(RunChild)
{
Name = $"Child {i:00}"
};
threadList.Add(thread);
thread.Start();
}
while (!killAll)
{
var str = Console.ReadLine();
if (str.Equals("kill", StringComparison.InvariantCultureIgnoreCase))
{
killAll = true;
break;
}
int enteredId = -1;
if (int.TryParse(str, out enteredId))
{
id = enteredId;
}
}
foreach (var thread in threadList)
{
thread.Join();
}
Console.WriteLine("all dead");
}
}
}

Generate list with random numbers. Sort and total sum

I am new to programming and i would be delighted if someone could help me with the following problem.
I have problem with two methods.
First sum all of 50 random numbers in a list, second sort list with 50 random numbers.
I know how to do this when a list has for example four numbers{1,2,3,8} , but not with 50 random numbers.
It will be nice to use a constructor to this two methods, but I don't now how to do this.
Any help would be much appreciated.
class Class1
{
var rand = new Random();
public Class1( ) // how to use a constructor ?
public static List<double> TotalSum()
{
var alist = new List<double>();
for (int i = 0; i < 50; i++)
{
alist.Add(rand.Next(10));
}
Console.WriteLine("Total sum:", alist);
}
public static List<double> Sort()
{
var slist = new List<double>();
for (int i = 0; i < 50; i++)
{
slist.Sort(rand.Next(10)); // rand.Next does not work with Sort
Console.WriteLine(slist);
}
}
class Program
{
static void Main(string[] args)
{
Class1 show = new Class1();
show.TotalSum();
show.Sort();
}
}
}
just use the 2 methods
list.Sum();
list.Sort();
I suggest that you use rand.Next(Environment.TickCount) to get better random numbers, but at the end you need to use a different primitive value to store the result
class Program
{
static void Main(string[] args)
{
var array = Class1.Sort();
PrintArray(array);
Console.WriteLine();
Console.WriteLine("Total sum: {0}", Class1.TotalSum());
Console.ReadLine();
}
static void PrintArray(List<double> array)
{
foreach (var a in array)
Console.WriteLine(a);
}
}
public class Class1
{
public static double TotalSum()
{
var rand = new Random();
var alist = new List<double>();
for (int i = 0; i < 50; i++)
{
alist.Add(rand.Next(10));
}
return alist.Sum();
}
public static List<double> Sort()
{
var rand = new Random();
var slist = new List<double>();
for (int i = 0; i < 50; i++)
{
slist.Add(rand.Next(10)); // rand.Next does not work with Sort
}
slist.Sort();
return slist;
}
}
I think I got what you mean. You want a class that holds random list of numbers and you need some functions.
Its better to give more suitable name to your class. for example "RandomList" would be good.
You can't use Console.WriteLine(list) to print items of list. You have to iterate through items and print them in the way you want.
You can't use var to define fields. You have to write the type explicitly.
static keyword in other word is one for all instances. So if you dont use static it would be one per instance. Instance is just the object that you create with the constructor. Its better to declare random number generator as static, but other methods and list field should remain non static.
With the use of Linq Sum() you can get the sum of all the items from list. If you dont want to use Linq then you have to iterate through list and add each item to value sum.
I commented the code to explain things.
class Program
{
static void Main(string[] args)
{
RandomList show = new RandomList(50, 10);
show.TotalSum();
show.Sort();
}
}
class RandomList
{
private static Random rand = new Random();
private List<double> _list; // this field holds your list
public RandomList(int length , int maxValuePerItem) // this is the constructor
{
_list = new List<double>();
// populate list
for (int i = 0; i < length; i++)
{
_list.Add(rand.Next(maxValuePerItem));
}
}
public void TotalSum()
{
Console.WriteLine("Total sum: {0}", _list.Sum()); // {0} is required to specify the place to put _list.Sum() inside string.
// without linq way
// int sum = 0;
// foreach(var i in _list) sum += i;
}
public void Sort()
{
_list.Sort();
foreach (var d in _list)
{
Console.Write(d + " , "); // you need to print this in the way you want.
}
}
}
A constructor is typically used to initialize some values. In your case, you should use it to initialize your list, that is, to load it with 50 random numbers. We will also change it so that the list is a property of the class.
Note that the methods shouldn't be static, since they are acting on a property of the class. We are also using the built-in methods of List (Sum() and Sort()) instead of rolling our own.
class Class1
{
var rand = new Random();
var alist;
public Class1() // constructor creates a new list and initializes it
{
alist = new List<double>();
for (int i = 0; i < 50; i++)
{
alist.Add(rand.Next(10));
}
}
public List<double> TotalSum()
{
Console.WriteLine("Total sum:", alist.Sum());
}
public List<double> Sort()
{
alist.Sort();
for (double num in alist)
{
Console.WriteLine(num.ToString());
}
}
class Program
{
static void Main(string[] args)
{
Class1 show = new Class1();
show.TotalSum();
show.Sort();
}
}
}
Because you asked how to use the constructor I tried to rewrite your code in a way that the constructor do something of usefull. I added a property and a local variable plus another function.
public class Class1
{
Random rand = new Random();
List<double> alist { get; set; }
public Class1(int howmany = 50)
{
alist = new List<double>();
for (var i = 0; i < howmany; i++)
{
alist.Add(rand.NextDouble());
}
}
public void Sort()
{
alist.Sort();
}
public void printTotalSum()
{
Console.WriteLine("Total sum: {0}", alist.Sum());
}
public void printList() {
Console.WriteLine("The list contains:");
for (int i = 0; i < alist.Count; i++)
{
Console.WriteLine(alist[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
Class1 show = new Class1(10);
show.printTotalSum();
show.Sort();
show.printList();
}
}

C# Accessing a list from multiple methods

I am looking to access a list from different methods in the same class.Is there an easier way to access the movieTitle list without making a new list for each method? Do I have to make a reference to the list in every method? or should I put them all into a separate class all together? My overall goal is to have a option menu that gets input from the user and depending on the input calls a method to list all movies, add a movie to the list, and pick a random movie from the list.
class Program
{
static void Main(string[] args)
{
optionMenu();
Console.Read();
}
static void optionMenu()
{
Console.Write("(a)LIST MOVIES|(b)ADD Movie|(c)RANDOM MOVIE");
string ui = Console.ReadLine();
if (ui == "a") { printNames(); }
else if (ui == "b") { addMovie(); }
else if (ui == "b") { randomPickMovie(); }
else { optionMenu(); }
}
static void printNames()
{
List<string> movieTitles = new List<string>();
/*list.....
/ movieTitles.Add("Jurassic Park");
/..........
/..........
*/..........
Console.WriteLine("Movies in your list...");
for (int i = 0; i < movieTitles.Count;i++)
{
Console.WriteLine("\t-" + movieTitles[i]);
}
}
static void addMovie()
{
Console.WriteLine("Enter a title:");
string newTitle = Console.ReadLine();
//I can't say...
//movieTitles.Add(newTitle);
//...? do I need to make an instance of the list?
}
static void randomPickMovie()
{
Random r = new Random();
int random = r.Next();
Console.WriteLine(movieTitle[random]);
//same here. How do I access the movie titles in the printName() method so
//I can randomly pick a movie from the list?
}
}
Below is one way make the movie list shared. This declares and initializes the list as a static member of the class (instead of a local variable in the methods).
This approach works well for simple programs, but having global state in a large program can be problematic because it becomes difficult to see which methods affect which global state so bugs can easily creep in. See below for another approach.
class Program
{
static void Main(string[] args)
{
...
}
static List<string> movieTitles = new List<string>();
static void optionMenu()
{
...
}
static void printNames()
{
Console.WriteLine("Movies in your list...");
for (int i = 0; i < movieTitles.Count;i++)
{
Console.WriteLine("\t-" + movieTitles[i]);
}
}
static void addMovie()
{
movieTitles.Add(newTitle);
}
static void randomPickMovie()
{
...
}
}
Another approach is to pass the data from one method to another. This makes it more obvious to see what methods use the movieList. It also allows us to specify additional restrictions, e.g. you can see that printNames only needs a read-only version of the list so you know that printNames can't modify the list. This approach is a little more work but it's a good habit to get into because it reduces bugs in the long term.
class Program
{
static void Main(string[] args)
{
...
}
static void optionMenu()
{
List<string> movieTitles = new List<string>();
string ui = Console.ReadLine();
if (ui == "a") { printNames(movieTitles); }
else if (ui == "b") { addMovie(movieTitles); }
else if (ui == "b") { randomPickMovie(movieTitles); }
else { optionMenu(); }
}
static void printNames(IReadOnlyList<string> movieTitles)
{
Console.WriteLine("Movies in your list...");
for (int i = 0; i < movieTitles.Count;i++)
{
Console.WriteLine("\t-" + movieTitles[i]);
}
}
static void addMovie(List<string> movieTitles)
{
movieTitles.Add(newTitle);
}
static void randomPickMovie(List<string> movieTitles)
{
...
}
}
See https://stackoverflow.com/questions/13295319/instance-field-vs-passing-method-parameter for another user's point of view on which approach is better.
as for me, I prefer doing it this way.
class Program
{
static void Main(string[] args)
{
Movie movie = new Movie();
int x = 0;
while (x < 1)
{
movie.optionMenu(); Console.Write("Do you want to exit?");
string response = Console.ReadLine();
if (response == "Y") { x = 1; }
}
Console.Read();
}
}
class Movie
{
public List<string> movieTitles { get; set; }
public Movie()
{
movieTitles = new List<string>();
}
public void optionMenu()
{
Console.Write("(a)LIST MOVIES|(b)ADD Movie|(c)RANDOM MOVIE");
string ui = Console.ReadLine();
if (ui == "a") { printNames(); }
else if (ui == "b") { addMovie(); }
else if (ui == "c") { randomPickMovie(); }
else { optionMenu(); }
}
public void printNames()
{
Console.WriteLine("Movies in your list...");
for (int i = 0; i < movieTitles.Count; i++)
{
Console.WriteLine("\t-" + movieTitles[i]);
}
}
public void addMovie()
{
Console.WriteLine("Enter a title:");
string newTitle = Console.ReadLine();
if (newTitle != "")
{
movieTitles.Add(newTitle);
Console.WriteLine("New movie successfully added!");
}
else
{
Console.WriteLine("Cannot add empty movie. Add movie failed.");
}
}
public void randomPickMovie()
{
if (movieTitles.Count != 0)
{
Random r = new Random();
int random = r.Next(0, movieTitles.Count - 1);
Console.WriteLine(movieTitles[random]);
}
else { Console.WriteLine("Movie list is empty."); }
}
}
Answer for all your questions is create MovieTitles property of type List<string> and access it like this:
class Program
{
static void Main(string[] args)
{
optionMenu();
Console.Read();
}
static List<string> movieTitles;
static List<string> MovieTitles
{
get
{
if (movieTitles == null)
CreateMoviesList();
return movieTitles;
}
}
static void CreateMoviesList()
{
movieTitles = new List<string>();
/*list.....
/ movieTitles.Add("Jurassic Park");
/..........
/..........
*/
}
static void optionMenu()
{
Console.Write("(a)LIST MOVIES|(b)ADD Movie|(c)RANDOM MOVIE");
string ui = Console.ReadLine();
if (ui == "a") { printNames(); }
else if (ui == "b") { addMovie(); }
else if (ui == "b") { randomPickMovie(); }
else { optionMenu(); }
}
static void printNames()
{
Console.WriteLine("Movies in your list...");
for (int i = 0; i < MovieTitles.Count; i++)
{
Console.WriteLine("\t-" + movieTitles[i]);
}
}
static void addMovie()
{
Console.WriteLine("Enter a title:");
string newTitle = Console.ReadLine();
MovieTitles.Add(newTitle);
}
static void randomPickMovie()
{
Random r = new Random();
int random = r.Next();
Console.WriteLine(MovieTitles[random]);
}
}
CreateMoviesList() create list of movies only once and can be use to print movies, randon pick and add movies later on.

See any problems with this C# implementation of a stack?

I wrote this quickly under interview conditions, I wanted to post it to the community to possibly see if there was a better/faster/cleaner way to go about it. How could this be optimized?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Stack
{
class StackElement<T>
{
public T Data { get; set; }
public StackElement<T> Below { get; set; }
public StackElement(T data)
{
Data = data;
}
}
public class Stack<T>
{
private StackElement<T> top;
public void Push(T item)
{
StackElement<T> temp;
if (top == null)
{
top = new StackElement<T>(item);
}
else
{
temp = top;
top = new StackElement<T>(item);
top.Below = temp;
}
}
public T Pop()
{
if (top == null)
{
throw new Exception("Sorry, nothing on the stack");
}
else
{
T temp = top.Data;
top = top.Below;
return temp;
}
}
public void Clear()
{
while (top != null)
Pop();
}
}
class TestProgram
{
static void Main(string[] args)
{
Test1();
Test2();
Test3();
}
private static void Test1()
{
Stack<string> myStack = new Stack<string>();
myStack.Push("joe");
myStack.Push("mike");
myStack.Push("adam");
if (myStack.Pop() != "adam") { throw new Exception("fail"); }
if (myStack.Pop() != "mike") { throw new Exception("fail"); }
if (myStack.Pop() != "joe") { throw new Exception("fail"); }
}
private static void Test3()
{
Stack<string> myStack = new Stack<string>();
myStack.Push("joe");
myStack.Push("mike");
myStack.Push("adam");
myStack.Clear();
try
{
myStack.Pop();
}
catch (Exception ex)
{
return;
}
throw new Exception("fail");
}
private static void Test2()
{
Stack<string> myStack = new Stack<string>();
myStack.Push("joe");
myStack.Push("mike");
myStack.Push("adam");
if (myStack.Pop() != "adam") { throw new Exception("fail"); }
myStack.Push("alien");
myStack.Push("nation");
if (myStack.Pop() != "nation") { throw new Exception("fail"); }
if (myStack.Pop() != "alien") { throw new Exception("fail"); }
}
}
}
I think the Clear() method could be sped up significantly by changing it to top = null;. The entire stack will be garbage collected, and no loop required in the mean time.
You could simply use an array. The .NET array methods are really fast.
public class Stack<T>
{
private const int _defaultSize = 4;
private const int _growthMultiplier = 2;
private T[] _elements;
private int _index;
private int _limit;
public Stack()
{
_elements = new T[_defaultSize];
_index = -1;
_limit = _elements.Length - 1;
}
public void Push(T item)
{
if (_index == _limit)
{
var temp = _elements;
_elements = new T[_elements.Length * _growthMultiplier];
_limit = _elements.Length - 1;
Array.Copy(temp, _elements, temp.Length);
}
_elements[++_index] = item;
}
public T Pop()
{
if (_index < 0)
throw new InvalidOperationException();
var item = _elements[_index];
_elements[_index--] = default(T);
return item;
}
public void Clear()
{
_index = -1;
Array.Clear(_elements, 0, _elements.Length);
}
}
Might be preferable to use dynamic array as the data structure instead of a linked list. The array will have better locality of reference because the elements are next to each other. A stack doesn't need ability to delete elements in the middle, splicing etc. so an array suffices.

Categories

Resources