C # Array or List for one type of class

I have a class that contains several base types. (3x float, 2x int).

Now I need a collection that can contain several million instances of this class. I do not need derived types. All elements are in this class. In addition, the number of elements is fixed. In very rare cases, I plan to copy the entire list / array and change the copy. The initial list / array should be immutable, so I don't need to synchronize with other threads.

Now the question is:

  • Am I benefiting from an array instead of a list?
  • Store memory with an array?
  • How about speed?

I read that List in C # is also implemented as an array.

If it is C ++, I know that the array will contain the complete object. But I'm not sure how C # handles this. Will the C # array contain references to instances of the class, or will it contain the full data structure?

+6
source share
3 answers

The initial list / array should be immutable, so I don't need to synchronize with other threads.

Did you consider an immutable collection instead of T[] or List<T> ? ImmutableArray<T> will make the most sense. You can use ImmutableArray<T>.Builder to create a collection in an efficient way.

  • Am I benefiting from an array instead of a list?

If you do not need the number of elements to change, you should use Array. This will make it clear to everyone who looks at your code that you are not changing the number of elements.

  • Save memory with an array?

It depends on how you created the List<T> . Internally, when you add items to the List<T> , one after another, the size of the base array is changed using a 2 * factor: if there is not enough space for the new item, then the current base array is replaced with the new one with twice the size. So yes, you can save memory using the array directly, because you will not have allocated unnecessary memory. However, you can achieve the same by using List<T> , either by creating it using a constructor that uses the bandwidth of the list, or by calling the TrimExcess method TrimExcess items are added to the list.

  • How about speed?

Using an array, you save the logic that makes the methods, properties, and properties of the List<T> property translated into calls to the underlying array. But you do not have to worry about it, it will be imperceptible.

If it is C ++, I know that the array will contain the complete object. But I'm not sure how C # handles this. Will the C # array contain references to instances of the class, or will it contain the full data structure?

It depends. If you specify your type as a reference type (a class ), then both the array and the list will simply contain a link to certain elements. If you define it as a value type (a struct ), the array will contain the actual elements.

+6
source

In my opinion, you have a couple of requirements here, let me indicate them as markers:

  • A class with standard .net types such as float, integer, etc.

  • It is necessary to collect a collection for their storage (many of them, millions), effectively. You must choose between List, Array, etc.

  • The collection is basically fixed in size and rarely you need to resize the collection.

  • You want the collection to be immutable, thus stream-safe

Now List is also an array inside, just that it is dynamically reevaluated when and when items are added or removed. An important point:

  • For an array, all continuous distribution will happen at once, even if there are no elements, but for List, this will happen when and when elements are added. The list is therefore the best choice in this case.

  • The list provides flexibility for future requirements also in case collection should be flexible. In fact, it has software convenience, using the extension methods that also exist in the array, it is actually easy to convert between the array into a list or vice versa at any given time, therefore, as soon as the data freezes, then it is better to convert to an array and continue further, as you would know that there are no further changes.

  • In a pure performance assessment, arrays are better than a list; check the following links for more comparative information:

Array and List Performance

https://softwareengineering.stackexchange.com/questions/221892/should-i-use-a-list-or-an-array

  • To ensure thread safety, none of them will work, you either need to use the synchronization construct, for example Lock, Mutex, etc., or it is better to use collections under system.collections.concurrent , but it has a wide range, but none of they are not a list, the closest will be the ConcurrentBag, but this is not an ordered list, or you might want to consider my custom thread safe list under it, which uses ReaderWriterLock slim and works just as well as parallel collections:

      using System.Collections.Generic; using System.Threading; /// <summary> /// Thread safe version of the List using ReaderWriterLockSlim /// </summary> /// <typeparam name="T"></typeparam> public class ThreadSafeListWithRWLock<T> : IList<T> { // Internal private list which would be accessed in a thread safe manner private List<T> internalList; // ReaderWriterLockSlim object to take care of thread safe acess between multiple readers and writers private readonly ReaderWriterLockSlim rwLockList; /// <summary> /// Public constructor with variable initialization code /// </summary> public ThreadSafeListWithRWLock() { internalList = new List<T>(); rwLockList = new ReaderWriterLockSlim(); } /// <summary> /// Get the Enumerator to the Thread safe list /// </summary> /// <returns></returns> public IEnumerator<T> GetEnumerator() { return Clone().GetEnumerator(); } /// <summary> /// System.Collections.IEnumerable.GetEnumerator implementation to get the IEnumerator type /// </summary> /// <returns></returns> System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return Clone().GetEnumerator(); } /// <summary> /// Clone method to create an in memory copy of the Thread safe list /// </summary> /// <returns></returns> public List<T> Clone() { List<T> clonedList = new List<T>(); rwLockList.EnterReadLock(); internalList.ForEach(element => { clonedList.Add(element); }); rwLockList.ExitReadLock(); return (clonedList); } /// <summary> /// Add an item to Thread safe list /// </summary> /// <param name="item"></param> public void Add(T item) { rwLockList.EnterWriteLock(); internalList.Add(item); rwLockList.ExitWriteLock(); } /// <summary> /// Remove an item from Thread safe list /// </summary> /// <param name="item"></param> /// <returns></returns> public bool Remove(T item) { bool isRemoved; rwLockList.EnterWriteLock(); isRemoved = internalList.Remove(item); rwLockList.ExitWriteLock(); return (isRemoved); } /// <summary> /// Clear all elements of Thread safe list /// </summary> public void Clear() { rwLockList.EnterWriteLock(); internalList.Clear(); rwLockList.ExitWriteLock(); } /// <summary> /// Contains an item in the Thread safe list /// </summary> /// <param name="item"></param> /// <returns></returns> public bool Contains(T item) { bool containsItem; rwLockList.EnterReadLock(); containsItem = internalList.Contains(item); rwLockList.ExitReadLock(); return (containsItem); } /// <summary> /// Copy elements of the Thread safe list to a compatible array from specified index in the aray /// </summary> /// <param name="array"></param> /// <param name="arrayIndex"></param> public void CopyTo(T[] array, int arrayIndex) { rwLockList.EnterReadLock(); internalList.CopyTo(array,arrayIndex); rwLockList.ExitReadLock(); } /// <summary> /// Count elements in a Thread safe list /// </summary> public int Count { get { int count; rwLockList.EnterReadLock(); count = internalList.Count; rwLockList.ExitReadLock(); return (count); } } /// <summary> /// Check whether Thread safe list is read only /// </summary> public bool IsReadOnly { get { return false; } } /// <summary> /// Index of an item in the Thread safe list /// </summary> /// <param name="item"></param> /// <returns></returns> public int IndexOf(T item) { int itemIndex; rwLockList.EnterReadLock(); itemIndex = internalList.IndexOf(item); rwLockList.ExitReadLock(); return (itemIndex); } /// <summary> /// Insert an item at a specified index in a Thread safe list /// </summary> /// <param name="index"></param> /// <param name="item"></param> public void Insert(int index, T item) { rwLockList.EnterWriteLock(); if (index <= internalList.Count - 1 && index >= 0) internalList.Insert(index,item); rwLockList.ExitWriteLock(); } /// <summary> /// Remove an item at a specified index in Thread safe list /// </summary> /// <param name="index"></param> public void RemoveAt(int index) { rwLockList.EnterWriteLock(); if (index <= internalList.Count - 1 && index >= 0) internalList.RemoveAt(index); rwLockList.ExitWriteLock(); } /// <summary> /// Indexer for the Thread safe list /// </summary> /// <param name="index"></param> /// <returns></returns> public T this[int index] { get { T returnItem = default(T); rwLockList.EnterReadLock(); if (index <= internalList.Count - 1 && index >= 0) returnItem = internalList[index]; rwLockList.ExitReadLock(); return (returnItem); } set { rwLockList.EnterWriteLock(); if (index <= internalList.Count - 1 && index >= 0) internalList[index] = value; rwLockList.ExitWriteLock(); } } } 
0
source
  • Not really. Internal lists are just arrays, and also a counter for items in a list (not in an array). You will only have lower functionality.
  • If you know the number of at elements and set it during the initialization of the list, then you will not save any memory. Memory from using List is used very little, and although the size of the list is fixed, it does not depend on the number of elements. But if you do not specify the exact size of the list, then the size of the internal array will double each time you add a new element, and the number of elements in the list is larger than the size of the internal array.
  • No. In most cases, this is the same as working with regular arrays. Except for the time to recalibrate the internal array (but it is not too long and will not be executed when the List is fixed).

In C ++, Arrays and lists do not always store the entire complete object in it. They can only contain links. The same in .NET languages ​​(true even for managed C ++). Value types will be stored as is. Reference types will be saved ... as object references.

0
source

Source: https://habr.com/ru/post/977060/


All Articles