Różnica między Array i List w scali

142

W jakich przypadkach powinienem używać Array (Buffer) i List (Buffer). Jedyną różnicą, którą znam, jest to, że tablice są niezmienne, a listy są kowariantne. Ale co z wydajnością i innymi cechami?

Jeriho
źródło

Odpowiedzi:

155

Niezmienne struktury

Scala Listjest niezmienna rekurencyjne struktury danych, która jest taka zasadnicza struktura w Scala, które powinny (prawdopodobnie) będzie go używać znacznie więcej niż Array(co jest rzeczywiście zmienne - w niezmienny analogowy z Arrayto IndexedSeq).

Jeśli korzystasz z języka Java, oczywistą paralelą jest to, kiedy użyć LinkedListover ArrayList. Pierwsza z nich jest generalnie używana dla list, które są kiedykolwiek przeszukiwane (i których rozmiar nie jest znany z góry), podczas gdy druga powinna być używana do list, które mają znany rozmiar (lub maksymalny rozmiar) lub dla których ważny jest szybki losowy dostęp .

Zmienne struktury

ListBufferzapewnia konwersję w czasie stałym do a, Listktóra jest jedynym powodem do użycia, ListBufferjeśli taka późniejsza konwersja jest wymagana.

Skala Arraypowinna być implementowana w JVM przez tablicę Java, a zatem Array[Int]może być znacznie bardziej wydajna (jako int[]) niż a List[Int](która zapakuje zawartość, chyba że używasz najnowszych wersji Scali, które mają nową @specializedfunkcję) .

Uważam jednak, że użycie Arrays w Scali powinno być ograniczone do minimum, ponieważ wydaje się, że naprawdę musisz wiedzieć, co się dzieje pod maską, aby zdecydować, czy twoja tablica naprawdę będzie obsługiwana przez wymagany typ prymitywny, czy może być zapakowane jako typ opakowania.

oxbow_lakes
źródło
zobacz także stackoverflow.com/questions/3213368/… i stackoverflow.com/questions/2481149/… definicja „równa się” dla tablic jest taka, że ​​odnoszą się one do tej samej tablicy
oluies
130

Oprócz już opublikowanych odpowiedzi, oto kilka szczegółów.

Chociaż an Array[A]jest dosłownie tablicą Java, a List[A]jest niezmienną strukturą danych, która jest Nil(pusta lista) lub składa się z pary (A, List[A]).

Różnice w wydajności

                          Array  List
Access the ith element    θ(1)   θ(i)
Delete the ith element    θ(n)   θ(i)
Insert an element at i    θ(n)   θ(i)
Reverse                   θ(n)   θ(n)
Concatenate (length m,n)  θ(n+m) θ(n)
Count the elements        θ(1)   θ(n)

Różnice w pamięci

                          Array  List
Get the first i elements  θ(i)   θ(i)
Drop the first i elements θ(n-i) θ(1)
Insert an element at i    θ(n)   θ(i)
Reverse                   θ(n)   θ(n)
Concatenate (length m,n)  θ(n+m) θ(n)

Więc jeśli nie potrzebujesz szybkiego losowego dostępu, musisz liczyć elementy lub z jakiegoś powodu potrzebujesz destrukcyjnych aktualizacji, a Listjest lepsze niż Array.

Apocalisp
źródło
Czy ci OS muszą brać pod uwagę czas na skopiowanie listy? Zakładam, że robisz test w ten sposób, np list = list.drop(i). : . Albo, czy za maską pojawia się jakaś magia?
2
Uwzględnia to kopiowanie list i tablic, gdy jest to konieczne. Zwróć uwagę, że takie rzeczy jak dropnigdy nie muszą kopiować części listy, która nie została usunięta. Np. (x::xs).drop(1)To dokładnie xs, a nie „kopia” xs.
Apocalisp,
6
Te asymptotyki nie mają w ogóle nic wspólnego ze Scalą. Ta sama struktura danych w C będzie dokładnie tak samo szybka, aż do stałych współczynników.
Apocalisp,
1
@Apocalisp Czy masz referencje lub na jakich warunkach ustaliłeś te informacje?
Phil
1
@Phil To są asymptotyki, a nie pomiary. Sprawdzą się w każdych warunkach.
Apocalisp
18

Tablica jest zmienna, co oznacza, że ​​możesz zmienić wartości każdego indeksu, podczas gdy lista (domyślnie) jest niezmienna, co oznacza, że ​​nowa lista jest tworzona za każdym razem, gdy wykonujesz modyfikację. W większości przypadków jest to bardziej „funkcjonalny” styl pracy z niezmiennych typów danych i powinieneś spróbować skorzystać lista z konstrukcjami takimi jak yield, foreach, matchi tak dalej.

Ze względu na charakterystykę wydajności tablica jest szybsza z losowym dostępem do elementów, podczas gdy lista jest szybsza, gdy poprzedza (dodaje) nowe elementy. Iterowanie po nich jest porównywalne.

leonm
źródło
@leonm - apols, myślałem, że OP pyta wyłącznie o klasy * Buffer, zdaję sobie sprawę, że pytali również o te „normalne”!
oxbow_lakes
2
Zwykle szybsze jest dołączanie do ArrayBuffer niż dołączanie do listy (lub dodawanie elementu do ListBuffer), ponieważ listy wymagają utworzenia obiektu otoki, podczas gdy ArrayBuffer wystarczy skopiować obiekt (średnio około dwa razy) do nowej tablicy . Dwie kopie są zwykle szybsze niż tworzenie jednego obiektu, więc ArrayBuffer zwykle dołącza przed dodaniem listy.
Rex Kerr
tablica działa znacznie szybciej niż lista, gdy iterate over, z powodu pamięci podręcznej
Bin