PowerShell 數組、ArrayList 和集合:最佳實踐

在撰寫 PowerShell 腳本時,通常需要一種方式來存儲一組項目。一種常見的方法是使用數組或特定類型的 ArrayList。但是,數組到底是什麼呢?數組是一種設計用於存儲一組項目的數據結構。這可以包括相同和不同類型的項目。

數組在許多不同的編程語言中都被使用,PowerShell 也不例外。有許多方法可以創建、操作和優化數組。在本文中,您將學習有關 ArrayList、數組和集合的知識,以及在 PowerShell 中應用它們時的一些最佳實踐。

先決條件/要求

由於您只需使用 PowerShell 語言本身,因此不需要環境先決條件。您只需要擁有一台安裝了 PowerShell 的 Windows PC。具體要求如下:

  • Windows PowerShell 3 或更高版本
  • .NET Framework 4.5 或更高版本

想要獲得更多類似的技巧嗎?請查看我的個人 PowerShell 博客:https://nkasco.com/FriendsOfATA

使用 PowerShell 創建數組

有許多不同的方法可以使用 PowerShell 創建數組。讓我們假設您有一個需要以某種方式處理的名稱列表,如下所示。

John
Susie
Jim
Johnny
Carrie

通過逗號分隔元素構建數組

您可以最基本地將已知的輸入,逗號分隔,賦值給一個變量,如下所示。

$BasicArray = "John", "Susie", "Jim", "Johnny", "Carrie"

如果在PowerShell中對所有物件使用GetType()方法,您將會看到您已成功建立了一個陣列,這可以通過下面的BaseType屬性來證明。

PS51> $BasicArray.GetType()

IsPublic IsSerial Name                                     BaseType                                                    
-------- -------- ----                                     --------                                                    
True     True     Object[]                                 System.Array

使用子表達式運算符

您還可以通過子表達式運算符在PowerShell中建立陣列。當您不知道要添加多少項目到陣列時,這個概念通常被使用。結果在創建時可以包含零個或多個項目。

請注意下面創建了一個名為$MyArray的陣列,其中不包含任何元素。

#使用子表達式運算符創建一個空陣列
PS51> $MyArray = @()
PS51> $MyArray.count
0

使用範圍運算符

陣列不僅僅用於存儲字符串,如上所示,您還可以使用其他物件類型(如整數)創建陣列。

如果您需要一個按順序的整數陣列,可以簡化操作,使用範圍..運算符。下面的代碼一行就創建了一個包含整數2到5的陣列。

PS51> $NumberedArray = 2..5
PS51> $NumberedArray
2
3
4
5

創建PowerShell ArrayList集合

使用PowerShell ArrayList也是在PowerShell中存儲項目列表的一種方法。ArrayList類屬於.NET中的System.Collections命名空間。通過創建這個類型的新對象,您可以將對象存儲在ArrayList中。

下面您可以看到,您需要明確地使用New-Object cmdlet創建一個ArrayList對象,或者將標準陣列轉換為ArrayList對象。

請注意,在此情況下,BaseType 是一個物件,而上面的例子中,BaseType 是陣列,其繼承自Object類別。最終,PowerShell 提供了對 .NET 類型系統的存取。

PS51> $MyArrayList = New-Object -TypeName "System.Collections.ArrayList"
#將陣列轉換為 ArrayList 也是一個可行的選擇
PS51> $MyArrayList = [System.Collections.ArrayList]@()
PS51> $MyArrayList.GetType()

IsPublic IsSerial Name                                     BaseType                                                    
-------- -------- ----                                     --------                                                    
True     True     ArrayList                                System.Object

新增項目到陣列

在建立陣列時,您可以在創建時定義所有元素,或者隨時添加它們。

要將元素添加到現有集合中,可以使用+=運算符或Add方法。但要知道它們操作方式有很大不同。

當您使用@()創建標準陣列時,將使用+=運算符,但要將元素添加到 ArrayList 中,則使用Add方法。這兩種方法的不同之處在於,+=運算符實際上會破壞現有的陣列,並創建一個包含新項目的新陣列。

為了證明這一點,您將看到下面可以引用陣列或 ArrayList 的IsFixedSize屬性,以知道哪個是不可變的,哪個不是。

PS51> $BasicArray.IsFixedSize
True

PS51> $MyArrayList.IsFixedSize
False

由於基本陣列是一個固定大小的集合,因此無法修改它。

嘗試使用Add()方法將固定大小的陣列添加元素將導致錯誤。以下是一些成功向陣列添加項目的示例。

#不可行
$BasicArray.Add("Nate")

#可行
$BasicArray += "Nate"
$MyArrayList.Add("Nate")
$MyArrayList += "Nate"

從陣列中刪除項目

現在您對如何向陣列添加項目有了更好的理解,讓我們來介紹一些從陣列中刪除項目的方法。

由於基本陣列是固定的,您無法從中刪除項目。相反,您需要創建一個全新的陣列。例如,您可以通過創建一個條件語句,只匹配您想要包含的那些元素來從陣列中刪除單個元素。下面是一個示例。

$NewBasicArray = $BasicArray -ne "Nate"

由於ArrayList是可變大小的,您可以使用Remove()方法從中刪除元素。如果您計劃經常添加/刪除項目,使用ArrayList可能會對您有益。

$MyArrayList.Remove("Nate")

從陣列或ArrayList中檢索特定項目

您可以使用多種不同的方法從陣列或ArrayList中檢索特定項目。就像PowerShell中的其他對象一樣,您只需調用該對象即可訪問陣列的所有元素。

PS51> $BasicArray
John
Susie
Jim
Johnny
Carrie

也許您只需要檢索第一個元素,陣列始終具有以0表示的起點,表示陣列的第一個元素。要檢索陣列的第一個元素,請在方括號中指定索引號,如下所示。

PS51> $BasicArray[0]
John

相反地,您也可以使用破折號(負指示器)來倒序引用索引,以從陣列中調用最後X個元素。一種常見的方法是使用-1來找到陣列中的最後一個元素,如下所示。

PS51> $BasicArray[-1]
Carrie

您在上面學到的範圍運算符也可以用於通過調用元素來檢索陣列的對象。假設您想要檢索$BasicArray陣列中的前四個名字。

如下所示,您可以指定索引範圍0-3,這將返回前四個元素。

PS51> $BasicArray[0..3]
John
Susie
Jim
Johnny

使用PowerShell優化陣列

現在您已經掌握了如何創建和操作陣列的基礎知識,那麼應該使用哪一種呢?為了回答這個問題,讓我們通過一些使用Measure-Command cmdlet的例子來進行解釋。使用Measure-Command cmdlet,您將更好地了解命令處理元素所需的時間,因為它們被傳遞到管道中。

一般來說,如果您只有一小個對象集合,您可能不會注意到在如何操作陣列上的差異。然而,如果您有一個大型對象集合,了解這些差異以獲得最佳結果就很重要了。

現在讓我們應用您剛才在前一節中學到的有關+=和使用Add()方法處理50000個項目的循環之間的差異。

首先,按照下面的示例创建一个空数组和一个空的ArrayList。

PS51> $MyArray = @()
PS51> $MyArrayList = [System.Collections.ArrayList]@()

接下来,使用范围运算符和foreach循环在每个集合中填充50000个元素,如下所示。

@(0..50000).foreach({$MyArray += $_})
@(0..50000).foreach({$MyArrayList.Add($_)})

最后,将你的命令包装在一个表达式中,并将该表达式传递给Measure-Command cmdlet。通过使用Measure-Command执行表达式,你可以看到每个过程实际执行所需的时间。

请记住,正如你之前学到的那样,+=实际上是创建一个新数组,而不是向一个固定的数组追加。

PS51> Measure-Command -Expression {@(0..50000).foreach({$MyArray += $_})}
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 59
Milliseconds      : 58
Ticks             : 590585963
TotalDays         : 0.000683548568287037
TotalHours        : 0.0164051656388889
TotalMinutes      : 0.984309938333333
TotalSeconds      : 59.0585963
TotalMilliseconds : 59058.5963

PS51> Measure-Command -Expression {@(0..50000).foreach({$MyArrayList.Add($_)})}
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 139
Ticks             : 1399989
TotalDays         : 1.62035763888889E-06
TotalHours        : 3.88885833333333E-05
TotalMinutes      : 0.002333315
TotalSeconds      : 0.1399989
TotalMilliseconds : 139.9989

结果呢?近60秒对比139毫秒

正如你所见,对于大型集合,使用ArrayList要比使用固定大小的数组快得多。

虽然这只是一个基本示例,但它强调了在处理过程中理解代码的重要性。如果不正确理解,可能导致用户体验差。

如果你有任何现有的脚本可以通过使用ArrayList而不是数组来受益,这将是一个很好的机会,能够在一夜之间改进!

进一步阅读

想要更多类似的技巧吗?请访问我的个人PowerShell博客:https://nkasco.com/FriendsOfATA

Source:
https://adamtheautomator.com/powershell-array/