Jaka jest różnica między ArrayListi List<>w C #?
Czy tylko to List<>ma typ, a ArrayListnie ma?
List<>w ogóle, podczas gdy to dotyczy List<object>konkretnie
Jaka jest różnica między ArrayListi List<>w C #?
Czy tylko to List<>ma typ, a ArrayListnie 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 Tjest to typ wartości w ArrayListprzypadku). ArrayListpo prostu przechowuje objectreferencje. Jako zbiór rodzajową List<T>realizuje ogólne IEnumerable<T>interfejs i mogą być łatwo stosowane w LINQ (bez potrzeby dokonywania jakiejkolwiek Castlub OfTypepołączenia).
ArrayListnależy do dni, w których C # nie miał generycznych. Jest przestarzałe na korzyść List<T>. Nie powinieneś używać ArrayListw nowym kodzie kierowanym na .NET> = 2.0, chyba że musisz połączyć się ze starym API, który go używa.
ArrayListw ś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 ArrayListw 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 ArrayListMB 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.
ArrayListzapewnia 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
ArrayListjeś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/
ArrayListjest zbiorem danych różnych typów, podczas gdy List<>jest zbiorem podobnego rodzaju własnych oddziałów.
ArrayListnie 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ż ArrayListAdd tylko pobiera objectparametry, 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 ArrayListvs 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 ArrayListscenariuszu, 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 ArrayListi 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).ArrayListużywane tylko dla kompatybilności wstecznej. (nie jest to prawdziwa różnica, ale myślę, że to ważna uwaga).ArrayListnastępnieList<T>ArrayListma IsSynchronizedwłaściwość. Łatwo jest więc tworzyć i używać synchronizacji ArrayList. Nie znalazłem IsSynchronizednieruchomoś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 propArrayListma ArrayList.SyncRootwłaściwość, której można użyć do synchronizacji ( msdn ). List<T>nie ma SyncRootwł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
ArrayListklasy do nowych prac rozwojowych. Zamiast tego zalecamy użycieList<T>klasy ogólnej .ArrayListKlasa 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)