Array.Add vs + =

179

Znalazłem interesujące zachowanie w tablicach PowerShell, a mianowicie, jeśli zadeklaruję tablicę jako:

$array = @()

A potem spróbuję dodać do niego pozycje $array.Add("item")metodą, otrzymuję następujący błąd:

Wyjątek wywołujący funkcję „Dodaj” z argumentami „1”: „Kolekcja miała stały rozmiar”.

Jeśli jednak dodam pozycje używając $array += "item", pozycja zostanie zaakceptowana bez problemu i ograniczenie „stałego rozmiaru” nie wydaje się mieć zastosowania.

Dlaczego to?

malgca
źródło

Odpowiedzi:

254

Używając $array.Add()-method, próbujesz dodać element do istniejącej tablicy. Tablica to zbiór o ustalonym rozmiarze, więc zostanie wyświetlony błąd, ponieważ nie można jej rozszerzyć.

$array += $elementtworzy nową tablicę z tymi samymi elementami, co stara + nowy element, a ta nowa większa tablica zastępuje starą w $array-variable

Możesz użyć operatora + =, aby dodać element do tablicy. Kiedy go używasz, Windows PowerShell faktycznie tworzy nową tablicę z wartościami oryginalnej tablicy i wartością dodaną. Na przykład, aby dodać element o wartości 200 do tablicy w zmiennej $ a, należy wpisać:

    $a += 200

Źródło: about_Arrays

+= jest kosztowną operacją, więc jeśli chcesz dodać wiele elementów, powinieneś spróbować dodać je w jak najmniejszej liczbie operacji, np .:

$arr = 1..3    #Array
$arr += (4..5) #Combine with another array in a single write-operation

$arr.Count
5

Jeśli nie jest to możliwe, rozważ użycie bardziej wydajnej kolekcji, takiej jak Listlub ArrayList(zobacz drugą odpowiedź).

Frode F.
źródło
Dzięki :) Pomyślałem, że może to być coś takiego, ale pomyślałem, że będzie to nieefektywne przy dużych tablicach, więc zespół PowerShell zrobił coś innego.
malgca
6
dokładnie tak, staje się nieefektywne przy dużych tablicach, niestety aby obejść ten problem, musisz użyć innego typu: powershell.org/wp/2013/09/16/ ...
Nacht
3
To zależy. Jeśli zamierzasz dodawać i usuwać wielu członków, tak, spróbuj Listlub ArrayList. Będą znacznie szybsze. Osobiście używam +=i ustawiam tablice w 99% przypadków, ponieważ zwykle tworzę krótkie, jednorazowe skrypty, w których dodatkowe sekundy nie mają znaczenia. W przypadku dużych skryptów z dużą ilością dodawania / usuwania, w których chcę zoptymalizować i zaoszczędzić czas, używam Listlub ArrayList.
Frode F.
3
Ponieważ tablice mają zawsze stały rozmiar, czy ktoś wie, dlaczego ta Add()metoda istnieje?
JohnLBevan
4
Ponieważ jest dziedziczony z IList. Spróbuj Get-Member -InputObject @()to pokazaćAdd Method int IList.Add(System.Object value)
Frode F.
113

Jeśli chcesz mieć tablicę o rozmiarze dynamicznym, powinieneś zrobić listę. Nie tylko uzyskasz .Add()funkcjonalność, ale jak wyjaśnia @ frode-f, tablice dynamiczne są bardziej wydajne pod względem pamięci i tak czy inaczej są lepszą praktyką.

Jest tak łatwy w użyciu.

Zamiast deklaracji tablicy, spróbuj tego:

$outItems = New-Object System.Collections.Generic.List[System.Object]

Dodawanie elementów jest proste.

$outItems.Add(1)
$outItems.Add("hi")

A jeśli naprawdę chcesz mieć tablicę, kiedy skończysz, jest też funkcja do tego.

$outItems.ToArray()
Sebastian Kaczmarek
źródło
1
Próbowałem tego. Tworzę go za pomocą New-Object System.Collections.Generic.List [string], ale jeśli zrobię .GetType, to mówi mi, że jest to tablica.
Preza8
1
Czy próbowałeś użyć tej Add()funkcji? Mogę potwierdzić, że jeśli utworzysz ogólny Listobiekt, jak wspomniano powyżej, masz zmienną listę, dla której możesz dodawać i usuwać elementy za pomocą odpowiednio metod Add()i Remove().
Bender the Greatest
1
@ Preza8: (New-Object System.Collections.Generic.List[string]).GetType().Namedaje List`1mi się zgodnie z oczekiwaniami; być może zastosowałeś +=zmienną zawierającą listę (zamiast wywoływać .Add()metodę), w którym to przypadku wartość zmiennej rzeczywiście zostanie przekonwertowana na tablicę ( System.Object[]).
mklement0
Skrót:$a = new-object collections.generic.list[object]
Andrew
4

Najpopularniejszym idiomem do tworzenia tablicy bez użycia nieefektywnego +=jest coś takiego, na podstawie wyniku pętli:

$array = foreach($i in 1..10) { 
  $i
}
$array
js2010
źródło