Jaka jest różnica między ArrayList
i List<>
w C #?
Czy tylko to List<>
ma typ, a ArrayList
nie ma?
List<>
w ogóle, podczas gdy to dotyczy List<object>
konkretnie
Jaka jest różnica między ArrayList
i List<>
w C #?
Czy tylko to List<>
ma typ, a ArrayList
nie ma?
List<>
w ogóle, podczas gdy to dotyczy List<object>
konkretnie
Odpowiedzi:
Tak, właściwie. List<T>
jest klasą ogólną. Obsługuje przechowywanie wartości określonego typu bez rzutowania do lub z object
(co spowodowałoby obciążenie związane z boksem / rozpakowaniem, gdy T
jest to typ wartości w ArrayList
przypadku). ArrayList
po prostu przechowuje object
referencje. Jako zbiór rodzajową List<T>
realizuje ogólne IEnumerable<T>
interfejs i mogą być łatwo stosowane w LINQ (bez potrzeby dokonywania jakiejkolwiek Cast
lub OfType
połączenia).
ArrayList
należy do dni, w których C # nie miał generycznych. Jest przestarzałe na korzyść List<T>
. Nie powinieneś używać ArrayList
w nowym kodzie kierowanym na .NET> = 2.0, chyba że musisz połączyć się ze starym API, który go używa.
ArrayList
w środowisku wykonawczym. Statycznie będzie to wymagało obsady za pomocą ArrayList
.
Za pomocą List<T>
możesz zapobiec błędom przesyłania. Bardzo przydatne jest uniknięcie błędu rzutowania środowiska wykonawczego .
Przykład:
Tutaj (za pomocą ArrayList
) możesz skompilować ten kod, ale później zobaczysz błąd wykonania.
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
}
Jeśli używasz List
, unikniesz następujących błędów:
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;
}
Odniesienie: MSDN
Aby dodać do powyższych punktów. Używanie ArrayList
w 64-bitowym systemie operacyjnym zajmuje 2x pamięć niż w 32-bitowym systemie operacyjnym. Tymczasem ogólna lista List<T>
zużyje dużo mniej pamięci niż ArrayList
.
na przykład, jeśli użyjemy 19 ArrayList
MB w wersji 32-bitowej, to zajmie 39 MB w wersji 64-bitowej. Ale jeśli masz ogólną listę 8 List<int>
MB w wersji 32-bitowej, zajmie to tylko 8,1 MB w wersji 64-bitowej, co jest ogromną różnicą 481% w porównaniu do ArrayList.
Źródło: Lista ArrayList vs. ogólna dla typów pierwotnych i 64-bitowych
Kolejna różnica do dodania dotyczy synchronizacji wątków.
ArrayList
zapewnia pewne bezpieczeństwo wątków dzięki właściwości Synchronized, która zwraca opakowanie bezpieczne dla wątków wokół kolekcji. Opakowanie działa poprzez zablokowanie całej kolekcji przy każdej operacji dodawania lub usuwania. Dlatego każdy wątek, który próbuje uzyskać dostęp do kolekcji, musi czekać na swoją kolej, aby przejąć jedną blokadę. Nie jest to skalowalne i może powodować znaczne obniżenie wydajności dużych kolekcji.
List<T>
nie zapewnia synchronizacji wątków; kod użytkownika musi zapewniać całą synchronizację, gdy elementy są dodawane lub usuwane jednocześnie w wielu wątkach.
Więcej informacji tutaj Synchronizacja wątków w .Net Framework
ArrayList
jeśli można tego uniknąć, ale jest to głupi powód. W końcu opakowanie jest całkowicie opcjonalne; jeśli nie potrzebujesz blokowania lub potrzebujesz bardziej szczegółowej kontroli, nie używaj opakowania.
Prosta odpowiedź brzmi:
ArrayList arrayList = new ArrayList();
List<int> list = new List<int>();
arrayList.Add(1);
arrayList.Add("String");
arrayList.Add(new object());
list.Add(1);
list.Add("String"); // Compile-time Error
list.Add(new object()); // Compile-time Error
Przeczytaj oficjalny dokument Microsoft : https://blogs.msdn.microsoft.com/kcwalina/2005/09/23/system-collections-vs-system-collection-generic-and-system-collections-objectmodel/
Uwaga : powinieneś znać Generics, zanim zrozumiesz różnicę: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/
ArrayList
jest zbiorem danych różnych typów, podczas gdy List<>
jest zbiorem podobnego rodzaju własnych oddziałów.
ArrayList
nie są bezpieczne typu, podczas gdy List<T>
są bezpieczne typu. Prosty :).
Wydajność została już wspomniana w kilku odpowiedziach jako czynnik różnicujący, ale aby odpowiedzieć na pytanie „ Jak wolniejsze jest ArrayList
? ”I„ Dlaczego ogólnie jest wolniejszy?”, Spójrz poniżej.
Ilekroć typy wartości są używane jako elementy, wydajność dramatycznie spada ArrayList
. Rozważ przypadek zwykłego dodawania elementów. Ze względu na to, że boks trwa, ponieważ ArrayList
Add tylko pobiera object
parametry, Garbage Collector zostaje zmuszony do wykonania znacznie większej ilości pracy niż przy użyciu List<T>
.
Jaka jest różnica czasu? Co najmniej kilka razy wolniej niż z List<T>
. Wystarczy spojrzeć na to, co dzieje się z kodem dodającym wartości 10 milionów int do ArrayList
vs List<T>
:
To różnica czasu bieg 5x w kolumnie „oznacza”, podświetlone na żółto. Zwróć także uwagę na różnicę w liczbie wyrzucania elementów bezużytecznych dla każdego z nich, podświetloną na czerwono (liczba uruchomień GC / 1000).
Korzystanie z narzędzia do profilowania, aby zobaczyć, co się dzieje, pokazuje, że większość czasu spędza się na tworzeniu GC , a nie na dodawaniu elementów. Brązowe słupki poniżej przedstawiają blokowanie aktywności Garbage Collector:
Szczegółową analizę tego, co dzieje się w powyższym ArrayList
scenariuszu, napisałem tutaj https://mihai-albert.com/2019/12/15/boxing-performance-in-c-analysis-and-benchmark/ .
Podobne wyniki znajdują się w „CLR via C #” Jeffrey'a Richtera. Z rozdziału 12 (Ogólne):
[…] Kiedy kompiluję i uruchamiam kompilację wydania (z włączonymi optymalizacjami) tego programu na moim komputerze, otrzymuję następujące dane wyjściowe.
00: 00: 01.6246959 (GCs = 6) Lista <Int32>
00: 00: 10.8555008 (GCs = 390) ArrayList of Int32
00: 00: 02.5427847 (GCs = 4) Lista <String>
00: 00: 02.7944831 (GCs = 7 ) ArrayList of StringDane wyjściowe pokazują, że użycie ogólnego algorytmu List z typem Int32 jest znacznie szybsze niż użycie nietypowego algorytmu ArrayList z Int32. W rzeczywistości różnica jest fenomenalna: 1,6 sekundy w porównaniu do prawie 11 sekund. To ~ 7 razy szybciej ! Ponadto użycie typu wartości (Int32) z ArrayList powoduje wiele operacji bokserskich, co powoduje 390 wyrzucania elementów bezużytecznych. Tymczasem algorytm List wymagał 6 odśmiecania.
Myślę, że różnice między ArrayList
i List<T>
są:
List<T>
, gdzie T jest typem wartości, jest szybsze niż ArrayList
. To dlatego, żeList<T>
unika się bokowania / rozpakowywania (gdzie T jest typem wartości).ArrayList
używane tylko dla kompatybilności wstecznej. (nie jest to prawdziwa różnica, ale myślę, że to ważna uwaga).ArrayList
następnieList<T>
ArrayList
ma IsSynchronized
właściwość. Łatwo jest więc tworzyć i używać synchronizacji ArrayList
. Nie znalazłem IsSynchronized
nieruchomości dla List<T>
. Należy również pamiętać, że ten rodzaj synchronizacji jest stosunkowo nieefektywny, msdn ):
var arraylist = new ArrayList();
var arrayListSyncronized = ArrayList.Synchronized(arraylist
Console.WriteLine($"syncronized {arraylist.IsSynchronized}");
Console.WriteLine($"syncronized {arrayListSyncronized.IsSynchronized}");
var list = new List<object>();
var listSyncronized = ArrayList.Synchronized(list);
Console.WriteLine($"syncronized {list.IsSynchronized}");//error, no such prop
Console.WriteLine($"syncronized {list.IsSynchronized}");//error, no such prop
ArrayList
ma ArrayList.SyncRoot
właściwość, której można użyć do synchronizacji ( msdn ). List<T>
nie ma SyncRoot
właściwości, więc w poniższej konstrukcji musisz użyć jakiegoś obiektu, jeśli używasz List<T>
:
ArrayList myCollection = new ArrayList();
lock(myCollection.SyncRoot) // ofcourse you can use another object for this goal
{
foreach (object item in myCollection)
{
// ...
}
}
Jak wspomniano w dokumentacji .NET Framework
Nie zalecamy używania tej
ArrayList
klasy do nowych prac rozwojowych. Zamiast tego zalecamy użycieList<T>
klasy ogólnej .ArrayList
Klasa jest przeznaczony do przechowywania heterogenicznych zbiory przedmiotów. Jednak nie zawsze oferuje najlepszą wydajność. Zamiast tego zalecamy następujące czynności:
- W przypadku heterogenicznej kolekcji obiektów użyj typu
List<Object>
(w języku C #) lubList(Of Object)
(w języku Visual Basic).- Aby uzyskać jednorodną kolekcję obiektów, użyj
List<T>
klasy.
Zobacz także Nie należy używać kolekcji nietypowych
Za pomocą „Listy” możesz zapobiec błędom przesyłania. Bardzo przydatne jest uniknięcie błędu rzutowania środowiska wykonawczego.
Przykład:
Tutaj (używając ArrayList) możesz skompilować ten kod, ale później zobaczysz błąd wykonania.
// 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.
}
}
Dla mnie chodzi przede wszystkim o znajomość swoich danych. Jeśli nadal będę rozszerzać swój kod na podstawie wydajności, musiałbym wybrać opcję Lista jako sposób na odszyfrowanie moich danych bez niepotrzebnego kroku ciągłego zastanawiania się nad typami, zwłaszcza „Typami niestandardowymi”. Jeśli maszyna rozumie różnicę i może określić na podstawie tego, z jakiego rodzaju danymi faktycznie mam do czynienia, to dlaczego miałbym przeszkadzać i marnować czas na wahania oznaczeń „JEŚLI JESZCZE”? Moja filozofia polega na tym, aby maszyna działała dla mnie, a nie dla mnie? Znajomość unikatowych różnic w różnych poleceniach kodu obiektowego znacznie przyczynia się do zwiększenia wydajności kodu.
Tom Johnson (One Entry ... One Exit)