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命令或将标准数组转换为ArrayList对象来创建ArrayList对象。

通知,在这种情况下,BaseType 是一个对象,而上面的例子中,Arrays 的 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 方法。这两种方法的不同之处在于 += 运算符实际上 销毁 现有数组,并使用新项创建一个新数组。

为了演示,您将看到下面可以引用 IsFixedSize 属性,以了解数组或 ArrayList 哪个是不可变的。

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,您将更好地了解命令处理元素所花费的时间。

一般来说,如果您有一小组对象,您可能不会注意到操作数组的方式有多大差异。但是,如果您有一大组对象,了解差异以实现最佳结果就变得很重要。

让我们将刚刚在前一节学到的关于+=和使用循环处理50,000个项目的Add()方法的区别应用起来。

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

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

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

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

最后,将你的命令包装在一个表达式中,并将该表达式传递给 Measure-Command 命令。通过使用 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/