Lekce 8 - PowerShell - Kolekce objektů, II. část
V minulé lekci, PowerShell - Kolekce objektů, jsme se seznámili s první části PowerShell kolekcemi a vysvětlili jsme si práci s každou z nich.
V dnešní lekci kurzu PowerShell frameworku si dobereme typy kolekcí PowerShellu.
Dynamické pole
(System.Collections.ArrayList
)
Dynamické pole ArrayList
na rozdíl od
statického pole Array
nepodporuje ochranu datového typu.
Nicméně je možné vytvořit dynamické pole pomocí statického pole s
ochranou datového typu, viz. příklad níže:
[System.Collections.ArrayList]$Pole=[int[]]@(1,2,3)
Dynamické pole na rozdíl od statického pole (v PowerShellu pod datovým
typem [System.Collections.ArrayList]
) navíc umožňuje odebrání
prvku (elementů) z pole.
Deklarace dynamického pole se provádí podobně jako u statického pole s tím rozdílem, že tento datový typ musíme vynutit vždy, když potřebujeme dynamické pole:
[System.Collections.ArrayList]$Pole=@(1,2,3)
Základní operace s prvky v dynamickém poli
Dynamické pole podporuje všechny operace jako statické pole, navíc ještě však:
- odebrání prvku z pole - v případě dynamického pole se
toto provádí pomocí metod
.Remove()
,.RemoveAt()
aRemoveRange()
. - vymazání všech prvků z pole - toto se provádí pomocí
metody
.Clear()
. - obrácení pořadí prvků v poli - někdy je potřeba
otočit pořadí prvků v poli, tzn. prvek na prvním místě bude poslední a
obráceně. Toto se provádí pomocí metody
.Reverse()
.
Příklad odebrání prvků z pole:
[System.Collections.ArrayList]$Pole=1..10 $Pole.Remove(8) #odstraní prvek s hodnotou 8 z pole $Pole.RemoveAt(0) #odstraní prvek s indexem 0 (pozice v poli 1) $Pole.RemoveRange(0,4) #odstraní prvky s indexem 0 až 4 (pozice v poli 1 až 5)
Statické pole versus dynamické pole - performance
Když dojde na otázku rychlosti zpracování dat obou
datových typů, zde je dynamické pole jasný vítěz. Pojďme se podívat
proč. Nejprve však malá ukázka, kde otestujeme rychlost přidání stejného
počtu prvků do obou typů polí. K tomuto použijeme následující kousek
kódu a příkaz Measure-Command
, který měří délku exekuce
kódu:
$ArrayList = New-Object -TypeName 'System.Collections.ArrayList' $Array = @() Measure-Command { for ($i = 0; $i -lt 10000; $i++) { $null = $ArrayList.Add("Adding item $i") } } Measure-Command { for ($i = 0; $i -lt 10000; $i++) { $Array += "Adding item $i" } }
A zde je výsledek:
Windows PowerShell Days : 0 Hours : 0 Minutes : 0 Seconds : 0 Milliseconds : 53 Ticks : 531792 TotalDays : 6.155E-07 TotalHours : 1.4772E-05 TotalMinutes : 0.00088632 TotalSeconds : 0.0531792 TotalMilliseconds : 53.1792 Days : 0 Hours : 0 Minutes : 0 Seconds : 3 Milliseconds : 558 Ticks : 35581670 TotalDays : 4.11824884259259E-05 TotalHours : 0.000988379722222222 TotalMinutes : 0.0593027833333333 TotalSeconds : 3.558167 TotalMilliseconds : 3558.167
Jak můžeme vidět, v případě typu ArrayList
zabralo 53ms
přidat všechny prvky do tohoto pole. Nicméně pro typ Array
to
zabralo několikanásobně víc času, a to konkrétně 3s a 56ms, což je
opravdu markantní rozdíl.
Tento rozdíl v performanci je způsoben systémem, jak pracuje statické a dynamické pole při přidávání dalších prvků:
- statické pole: velikost statického pole je
fixní. To lze ověřit pomocí vlastnosti
.IsFixedSize
. Při přidání dalšího prvku se vezme poslední přidaný prvek a původní obsah pole a toto pole se v paměti vytvoří znovu i s nově přidaným prvkem. Tudíž při každém přidání nového prvku je toto pole prakticky deklarováno v paměti znovu jako nové pole, pokaždé s fixní velikostí. - dynamické pole: velikost dynamického pole není
fixní, tudíž může v paměti počítače expandovat. Toto lze
taktéž zobrazit pomocí vlastnosti
.IsFixedSize
. Při přidání dalšího prvku do tohoto pole se tento nový prvek přidá do existujícího pole a jeho velikost se v paměti zvětší.
Statické pole versus dynamické pole - závěr
Uvedeme si pár pro a proti použití statického oproti dynamického pole.
Statické pole:
+
podpora ochrany datového typu+
použit jako defaultní datový typ PowerShellu-
performance je opravdu pomalá v porovnání s dynamickým polem-
nelze odebírat prvky z pole
Dynamické pole:
+
několikanásobně lepší performance v porovnání se statickým polem+
lze odebírat prvky z pole a to hned několika různými způsoby+
metoda.Reverse()
, která se občas hodí-
chybí podpora ochrany datového typu, nicméně toto lze s menším úsilím implementovat
Hash
tabulka (hashtable
) versus tříděný slovník
(OrderedDictionary
)
Ať už HashTable
nebo OrderedDictionary
, oba tyto
typy jsou podobné typu pole s tím rozdílem, že struktura
prvku v hash tabulce je ve formátu klíč=hodnota klíče
(Key=Value
).
Defaultně není hash tabulka tříděná, tudíž prvky na výstupu nemusí být v tom pořadí, v jakém byly do této tabulky přidány. Což ovšem nemusí být vždy problém.
K tomu, abychom docílili stejného pořadí na výstupu i na vstupu, použijeme tříděný slovník.
HashTable
je deklarována pomocí výrazu@{}
.OrderedDictionary
je deklarován pomocí výrazu[ordered]@{}
.
Pro oba typy však platí stejný zápis dat do této tabulky.
HashTable
:
$HashTable = @{ Name = 'Vojtech' Surname = 'Kasny' Age = 39 }
OrderedDictionary
:
$OrderedHashTable = [ordered]@{ Name = 'Vojtech' Surname = 'Kasny' Age = 39 }
Podívejme se na výstup proměnné $HashTable
, která není
tříděná a proměnné OrderedHashTable
, která už tříděná
je:
Windows PowerShell $HashTable Name Value ---- ----- Name Vojtech Age 39 Surname Kasny $OrderedHashTable Name Value ---- ----- Name Vojtech Surname Kasny Age 39
Základní operace s hash tabulkou
Platí pro oba typy, tříděné i netříděné tabulky.
Deklarace
Provádí se pomocí výrazu @{}
a může být buď prázdná
nebo s definovanými hodnotami.
Prázdná hash tabulka:
$EmptyHashtable = @{}
Definovaná hash tabulka, která je už setříděná:
$CustomHashtable = [ordered]@{ Color = 'Red' Doors = 4 Manufactured = Get-Date }
Přidání záznamu
Provádí se pomocí metody
.Add('<key>','<value>')
:
$CustomHashtable.Add('Engine','Petrol')
Jméno klíče musí být v tabulce unikátní.
Odebrání záznamu
Provádí se pomocí metody .Remove('<key>')
:
$CustomHashtable.Remove('Doors')
Využití hash tabulky v praxi
Hash tabulku lze v praxi využít efektivně ke dvěma věcem:
- k PSCustomObjectu
- a k tzv. splattingu
PSCustomObject
Hash tabulku použijeme jako vstupní data pro nový custom objekt, se kterým už umíme pracovat:
$obj = New-Object psobject -Property $CustomHashtable
Výstup:
Windows PowerShell $obj Color Manufactured Engine ----- ------------ ------ Red 9/26/2020 3:29:36 PM Petrol
Splatting
Tímto označením není myšleno nic jiného, než předání vstupních
parametrů příkazu pomocí hash tabulky. Tato tabulka je poté předána
danému příkazu pomocí znaku @
(viz. příklad níže). Toto se
provádí pro lepší čitelnost kódu a to tehdy, když počet vstupních
parametrů na jednom řádku přesáhne únosnou mez.
Co je únosná mez záleží čistě na daném vývojáři. Já osobně mám tuto mez nastavenou ve Visual Studio Code na 160 znaků.
#Tento zápis je sice funkční, nicméně obtížně čitelný (Get-WmiObject -Query "select WorkingSetSize from win32_process where name='system'" -EnableAllPrivileges -ComputerName $env:COMPUTERNAME -Verbose -ErrorAction Stop -Namespace root/cimv2).WorkingSetSize #Stejný zápis pomocí tabulky hash (zde stačí netříděná tabulka) $CommandArguments = @{ Query = "select WorkingSetSize from win32_process where name='system'" EnableAllPrivileges = $true ComputerName = $env:COMPUTERNAME Namespace = 'root/cimv2' Verbose = $true ErrorAction = 'Stop' }
Příkaz Get-WmiObject
stačí poté již zavolat a předat
argumenty pomocí splattingu:
Windows PowerShell (Get-WmiObject @CommandArguments).WorkingSetSize 20189184
Nutno ještě zmínit, že všechny vstupní parametry, ať již skriptu či
custom funkce, jsou v kódu reprezentovány systémovou proměnnou
$PSBoundParameters
, která je typu
PSBoundParametersDictionary
a lze ji využít stejně jako tabulka
hash:
function Write-PassedParameters { param ( [parameter(Mandatory)][int[]]$Port, $ComputerName=$env:COMPUTERNAME ) Write-Output $PSBoundParameters }
Výstup:
Windows PowerShell Write-PassedParameters 1,2,3,4 -ComputerName localhost Key Value --- ----- ComputerName localhost Port {1, 2, 3, 4}
A ještě ukázka jednoduché konverze na datový typ
PSCustomObject
:
Windows PowerShell New-Object psobject -Property (Write-PassedParameters 1,2,3,4 -ComputerName localhost) ComputerName Port ------------ ---- localhost {1, 2, 3, 4}
V další lekci, PowerShell - Cykly, se seznámíme s PowerShell cykly.