2014-10-22 1 views
6

Ho una classe che contiene alcuni tipi di base. (3x float, 2x int).C# Array o Elenco per un tipo di classe singola

Ora ho bisogno di una raccolta che possa contenere diverse milioni di istanze di questa classe. Non ho bisogno di tipi derivati. Tutti gli elementi sono esattamente da questa singola classe. Ancora più il numero di elementi è fisso. In casi molto rari ho intenzione di copiare l'intera lista/matrice e modificare la copia. La lista/matrice originaria deve essere immutabile, quindi non ho bisogno di sincronizzarmi con altri thread.

Ora la domanda sono:

  • sono i vantaggi da un array invece di una lista?
  • È possibile salvare la memoria utilizzando una matrice?
  • E la velocità?

Ho letto che un elenco in C# è implementato internamente come matrice.

Se fosse C++, so che l'array potrebbe contenere l'oggetto completo. Ma non sono sicuro di come C# gestisca questo. Un array C# manterrà solo riferimenti alle istanze di classe o conserverà la struttura dati completa?

+1

In .NET entrambi gli array e le liste contengono riferimenti –

+0

Penso che troverete alcune risposte [qui] (http://stackoverflow.com/questions/434761/array-versus-listt-when-to-use-which) – szpic

+0

Per quanto riguarda le domande su memoria e velocità, le hai confrontate tu stesso? – rhughes

risposta

6

L'elenco/matrice originario deve essere immutabile, quindi non è necessario sincronizzarlo con altri thread.

Hai considerato una collezione immutabile invece di T[] o List<T>? ImmutableArray<T> sarebbe più sensato. È possibile utilizzare ImmutableArray<T>.Builder per creare la raccolta in modo efficiente.

  • faccio a beneficiare di un array invece di una lista?

Se non è necessario il numero di elementi per cambiare si dovrebbe usare Array. Metterà in chiaro a tutti coloro che guardano il tuo codice che non stai modificando il numero di elementi.

  • faccio a salvare la memoria utilizzando un array?

Dipende da come ci si crea il List<T>. Internamente, quando si aggiungono elementi a List<T> una alla volta, la dimensione dell'array sottostante cambia usando il moltiplicatore 2 *: quando non c'è abbastanza spazio per il nuovo elemento, l'array sottostante corrente viene sostituito da uno nuovo con il doppio della dimensione. Quindi sì, è possibile salvare la memoria utilizzando direttamente la matrice, poiché non sarà allocata alcuna memoria non necessaria. Tuttavia, è possibile ottenere lo stesso utilizzando List<T>, sia creandolo utilizzando il costruttore che prende la capacità dell'elenco o chiamando il metodo TrimExcess dopo che tutti gli elementi sono stati aggiunti all'elenco.

  • E la velocità?

che utilizza la matrice si risparmia logica che rende List<T> i metodi, le proprietà e le chiamate di proprietà indicizzatore tradotti alle chiamate matrice sottostanti. Ma non dovresti preoccupartene, sarà impercettibile.

Se fosse C++, so che l'array potrebbe contenere l'oggetto completo. Ma non sono sicuro di come C# gestisca questo. Un array C# manterrà solo riferimenti alle istanze di classe o conserverà la struttura dati completa?

Dipende. Se si definisce il tipo come tipo di riferimento (a class), sia la matrice che l'elenco terrebbero semplicemente un riferimento a elementi particolari. Se lo si definisce come tipo di valore (a struct), array manterrà gli elementi effettivi.

+0

La lista o la matrice immutabili sono noti per avere problemi di prestazioni, non sono la scelta migliore, poiché ogni volta creerebbero una copia completamente nuova e se gli elementi sono in milioni anche quelli negativi.Le raccolte simultanee ottengono punteggi più alti rispetto alla lista o all'array Immutable, ma le raccolte simultanee non hanno un elenco o una matrice sostitutiva –

+0

Se il codice è composto da semplici calcoli senza cose come I/O e usa matrici/liste pesantemente, ci si può aspettare che un array sia IME circa due volte più veloce. La differenza può essere più grande; se sei legato alla CPU è certamente una vittoria facile che potresti anche prendere. –

+0

@MrinalKamboj Sì, è vero. È possibile ridurre al minimo l'overhead di creazione utilizzando la classe builder. Ma anche in quel caso l'accesso a quella struttura non sarà veloce quanto l'accesso a un array semplice. La domanda è cosa è più importante per determinati casi d'uso: se hai bisogno della soluzione più veloce, usa un array, – MarcinJuraszek

0

A mio avviso si dispone di due requisiti qui, mi permetta di specificarli come punti elenco:

  • classe con tipi .NET standard come float, integer ecc

  • bisogno di una raccolta di memorizzali (molti di loro, milioni), in maniera efficiente. Bisogno di decidere tra List, Array ecc.

  • La raccolta è per lo più di dimensioni fisse e raramente è necessario modificare la dimensione della raccolta.

  • Vuoi raccolta per essere immutabile, quindi infilare sicuro

Ora Elenco è anche un array internamente, solo che è dinamicamente ri-considerevole, come e quando gli elementi vengono aggiunti o rimossi. Punto importante è:

  • per una matrice tutta la dotazione continuo avverrà in un colpo solo, anche se gli elementi non ci sono, ma per la Lista che sarebbe successo, come e quando vengono aggiunti elementi. L'elenco è quindi una scelta migliore in questo caso

  • Elenco offre anche la flessibilità per il requisito futuro nel caso in cui la raccolta debba essere resa flessibile. In effetti ha la convenienza programmatica usando i metodi di estensione, che è anche presente in array, infatti è facile da convertire tra array in elenco o viceversa in qualsiasi punto del tempo, quindi una volta che il blocco dei dati è meglio per convertire in array e procedere ulteriormente, come sapresti non ci sono ulteriori cambiamenti.

  • In prospettiva puro prestazioni, matrice punteggi meglio di una lista controlla seguente link per ulteriori informazioni comparative:

Performance of Arrays vs. Lists

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

  • Per sicurezza filo, nessuno di loro avrebbe funzionato, è necessario utilizzare il costrutto di sincronizzazione come Lock, Mutex etc o meglio andare per le raccolte sotto lo system.collections.concurrent hanno una vasta gamma ma nessuna è esattamente una lista, la più vicina sarebbe ConcurrentBag ma si tratta di una lista non ordinata, o si potrebbe voler considerare la mia lista di thread personalizzati al di sotto, che usa ReaderWriterLock slim e funziona come buone collezioni come concorrenti:

    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
    Non
  1. davvero. Gli elenchi interni sono solo semplici matrici e inoltre contano per gli elementi nell'elenco (non nell'array). Avrai solo funzionalità più povere.
  2. Se si conosce il conteggio degli elementi e lo si imposta durante l'inizializzazione dell'elenco, non verrà salvata alcuna memoria. La memoria dall'uso dell'elenco è molto piccola e, mentre la dimensione dell'elenco è fissa, non dipende dal numero di elementi. Ma se non si specifica la dimensione esatta dell'elenco, la dimensione dell'array interno verrà raddoppiata ogni volta che si aggiunge un nuovo elemento e il conteggio degli elementi dell'elenco è maggiore della dimensione dell'array interno.
  3. No. La maggior parte del tempo è lo stesso che funziona con gli array normali. Tranne i tempi di ridimensionamento dell'array interno (ma non è troppo lungo e non si verifica quando List è fisso).

Nelle matrici e negli elenchi C++ non sempre memorizza l'intero oggetto completo al suo interno. Possono contenere solo riferimenti. Stessa cosa nei linguaggi .NET (vero anche per C++ gestito). I tipi di valore verranno memorizzati così come sono. I tipi di riferimento saranno archiviati ... come riferimenti agli oggetti.