Powershell数据容器:数组、ArrayList 与 哈希表
李俊才(jcLee95) 的个人博客
已入驻阿里云
邮箱 :291148484@163.com
本文地址:
- https://developer.aliyun.com/article/
- https://blog.csdn.net/qq_28550263/article/details/125723259
目 录
- 2.3.1 向 ArrayList 尾插元素
- 2.3.2 替换 ArrayList 中的元素
- 2.3.3 移除 ArrayList 中的元素
- 2.3.4 索引 ArrayList 中的元素
- 2.3.5 反转 ArrayList 中的元素
- 2.3.6 对 ArrayList 切片
- 2.3.7 按照元素值返回索引
- 2.3.8 在指定位置插入元素
- 2.3.9 清空 ArrayList
1. 数组
Powershell 中,提供了数组,其类型为 System.Array
。这个原生数组功能并不完善,仅在某些不是很复杂的脚本中使用。更多时候,我们会使用 下一小节 中所介绍的 ArrayList。
1.1 创建 Powershell 数组
使用@(...)
(…表示任意的元素)语法可以创建 Powershell 数组,例如:
$a = @(0,1,2,3,4,5,6)
在上例中,这个数组一共有7个元素,分别是1,2,3,4,5,6。如果你需要创建的是一个空数组,不列写任何元素即可,如:
$a = @()
1.2 向数组中添加元素
Poweshell数组有Add()
方法可以为数组添加一个元素,这个元素将添加到数组的末尾,并使数组的长度加1。但是对于使用@()
语法创建的数组是 固定长度数组,因此如果按照下面这种方法来插入元素会报错:
$a = @(); $a.Add(1);
Out[]:
MethodInvocationException: Exception calling “Add” with “1” argument(s): “Collection was of a fixed size.”
由于原数组是没有办法再修改其长度了,因此如果你一定要向数组中添加元素,这时只能通过重新创建一个包含所有元素以及新元素的数组来模拟数组中添加元素。因此 你可以参考 1.7 合并两个数组 小节中使用 +
运算符的方式来构建新数组。事实上,当 +
运算符右侧为非数组元素时,它会视为只有一个元素的数组,采用数组合并的逻辑来返回一个全新的数组。
1.3 索引数组元素
Powershell 数组提供了 Get()
方法用以索引数组中某个元素的值,例如:
$a = @(0,1,2,3,4,5,6); $a.Get(3);
Out[]:
3
这和以下索引书写方式的效果是一样的:
$a = @(0,1,2,3,4,5,6); $a[3];
Out[]:
3
1.4 修改数组中的元素
使用Set()
方法可以用来修改数组中的元素,例如:
$a = @(0,1,2,3,4,5,6); $a.SetValue(2,9); $a
Out[]:
0 1 9 3 4 5 6
可以看到,数组的第3个元素(索引为2)由3
变成了9
。
还有一种更简单的方法是使用索引该值,和$a.SetValue(2,9)
等价的做法是$a[2] = 9;
,即:
$a = @(0,1,2,3,4,5,6); $a[2] = 9; $a
Out[]:
0 1 9 3 4 5 6
1.5 数组切片
从语法上看,数组切片与数组的索引类似,都是使用[]
来获取原数组的一部分。但是数组的切片返回的不是一个元素,而是数组中一段索引连续的元素构成的新数组。例如:
$a = @(0,1,2,3,4,5,6); $a[3..6]; # 获取数组 $a 中,从 $a[3] 到 $a[6] 的所有元素构成的新数组
Out[]:
3 4 5 6
1.6 删除数组中的元素
你无法使用 Remove
和 RemoveAt
方法来移除 Powershell 数组中的元素,因为 Powershell 数组是一个固定大小的集合。因此就地删除元素是不可能的,但这不妨碍我们构建一个没有被删除元素的新数组来模拟数组元素的删除。
# 依据索引模拟数组元素删除 # 规定使用 -1 表示移除最后一个元素 function Remove([int]$index, [System.Array]$ary){ $length = $ary.Count; if($index -gt $length -1){Write-Error 索引超出范围} elseif($index -lt -1){Write-Error 索引必须大于-1} else{ # 删除索引为 -1 相当于删除索引为 length-1 的元素,其余留下 if($index -eq -1){ return $ary[0..($length-2)] } # 删除的是第一个元素,其余留下 elseif($index -eq 0){ return $ary[1..($length-1)] } # 删除中间索引号为index的元素,其余留下 else { return ($ary[0..($index-1)] + $ary[($index+1)..($length-1)]) } } }
按照以下方法来调用:
# 删除最后一个元素 $a = @(0,1,2,3,4,5,6,7); Remove -index -1 -ary $a
Out[]:
0 1 2 3 4 5 6
【说明】:Powershell 数组完全支持负数索引,但我个人更习惯仅仅使用 -1 获取最后数组的一个元素。
1.7 合并两个数组
1.7.1 使用 +
运算符
若要将两个数组合并为单个数组,请使用加号运算符 (+
) 。 以下示例创建两个数组,将其合并,然后显示生成的组合数组。例如:
$a=@(1,2,3); $b=@(4,5,6); $c = $a+$b;
其返回结果为包含两个数组所有元素,即数组 c 是由 1、2、3、4、5、6 构成的新数组。
1.7.2 使用 +=
运算符
你也可以使用 +=
来合并数组,例如:
$a=@(1,2,3); $b=@(4,5,6); $a+=$b
这相当于新创建了一个数组 c ,其元素为 1、2、3、4、5、6,在将数组 c 中的元素赋值给数组a,因此a的元素为 1、2、3、4、5、6。注意这里并不是对原数组 a 进行就地修改。
1.8 判断数组是否包含某个元素
$a=@(1,2,3); $a.Contains(2);
Out[]:
True
1.9 复制数组
数组是对象,可以使用Clone()
方法对其实现浅复制,例如:
$a = @(0,1,2,3,4,5,6,7); $b = $a.Clone();
这样 $b 就成立 $a 的一个浅复制副本。这里顺便提一下,在Powershell 中,万物皆对象,就像在 Python 这样的面向对象语言中一样。将表示一个对象的变量直接赋值给表示另一个对象的变量,仅仅是使被赋值的变量引用到赋值变量相同的对象的地址,因此这样的赋值方式不会获得一个全新的对象。例如:
$a = @(0,1,2,3,4,5,6,7); $b = $a; $b[2]=6; $a
Out[]:
0 1 6 3 4 5 6 7
可以看到,虽然我们使用$b[2]=6;
而不是$a[2]=6;
,但是$a
的索引为2的元素却被修改了——这里的根本原因是$b = $a;
仅仅将$a
引用的对象的地址传递给了$b
。
1.10 枚举数组
$a = @(0,1,2); $b = $a.GetEnumerator(); $b
Out[]:
0 1 2
$b.GetType()
Out[]:
IsPublic IsSerial Name BaseType -------- -------- ---- -------- False False ArrayEnumerator System.Object
1.11 数组元素类型约束
[int[]] $intA = 1, 2, 3; $intA[1] = "A";
这将导致报错,因为限制了必须为 int 数组,不能再将元素该为字符串:
InvalidArgument: Cannot convert value “A” to type “System.Int32”. Error: “Input string was not in a correct format.”
1.12 多维数组平展
1.13 关于数组表达式
数组表达式的语法格式为:
@( new-lines~opt~ statement-list~opt~ new-lines~opt~ )
1.14 Powershell 数组 的成员
成员名 | 类型 | 描述 |
Item |
ParameterizedProperty | 返回数组对应的 System.Management.Automation.PSMethodInfo 对象 |
Count |
Property | 返回表示数组长度的整数。 |
IsFixedSize |
Property | 返回一个 bool 值,该值指示 Array 是否具有固定大小 |
IsReadOnly |
Property | 返回一个 bool 值,该值指示 Array 是否只可读取 |
IsSynchronized |
Property | 返回一个 bool 值,该值指示是否同步对 Array 的访问(线程安全) |
Length |
Property | 返回表示数组长度的整数,和 Count 属性效果一样 |
LongLength |
Property | 获取一个 64 位整数,该整数表示 Array 的所有维数中元素的总数。 |
Rank |
Property | 获取 Array 的秩(维数)。 例如,一维数组返回 1,二维数组返回 2,依次类推。 |
SyncRoot |
Property | 获取可用于同步对 Array 的访问的对象。 |
Add | Method | 向数组尾插元素 |
Address | Method | |
Clear | Method | 清除数组的内容为默认值,不会改变数组的长度。若指定的为索引范围,它将数组中的某个范围的元素设置为每个元素类型的默认值。 |
Clone | Method | 创建 Array 的浅表副本 |
CompareTo | Method | |
Contains | Method | 判断数组中是否包含某元素 |
CopyTo | Method | 将当前一维数组的所有元素复制到指定的一维数组中。 |
Equals | Method | |
Get | Method | 接受一个整数作为数组索引,返回索引结果。 |
GetEnumerator | Method | 获取对象的枚举值。 |
GetHashCode | Method | 用于返回当前对象的哈希代码。 |
GetLength | Method | 获取数组在指定维度上的长度。如: $a = @(0,1,2,3,4,5,6); $a.GetLength(0) 的返回值为 7 。 |
GetLongLength | Method | 获取一个整数,该整数表示 Array 的指定维中的元素数 |
GetLowerBound | Method | 获取数组中指定维度第一个元素的索引:int GetLowerBound (int dimension)。 |
GetType | Method | 获取此对象的类型,返回的是一个对象。对于数组来说,返回的 BaseType字段 为 System.Array。 |
GetUpperBound | Method | 获取数组中指定维度第一个元素的索引 |
GetValue | Method | 用于索引数组中某个位序地元素 |
IndexOf | Method | 求元素在数组中第一次出现的位序号(即索引值),若元素不出现,则返回-1 。 |
Initialize | Method | 初始化数组。 |
Insert | Method | 向数组的指定索引处插入一个元素。 |
Remove | Method | void IList.Remove(System.Object value) |
RemoveAt | Method | 移除指定索引处的元素:void IList.RemoveAt(int index)。Powershell 的数组实际上不可使用该方法,原因是原生Powershell数组为定长数组,为一种长度固定的集合(Collection was of a fixed size)。 |
Set | Method | 将当前 Array 中的指定元素设置为指定值。void Set(int , System.Object) |
SetValue | Method | 将当前 Array 中的指定元素设置为指定值。void SetValue(System.Object value, int index) |
ToString | Method | 转换为字符串,对于 Powershell 数组来说,默认下总是返回 “System.Object[] ” |
2. ArrayList
2.1 如何使用 ArrayList
你应该感受到了 powershell 原生数组的诸多麻烦,即使是元素的增加和删除都显得特别费劲。但是好在powershell 是可以使用 .Net 的。因此完全可以使用 System.Collections.ArrayList
让我们繁琐的操作简单化。
要创建一个ArrayList数组,你大概需要多写一些:
$a = [System.Collections.ArrayList]@(1,2,3,4,5,6); $a
Out[]:
1 2 3 4 5 6
不过,往往在程序中为了方便,我们会使用 using
预先导入 System.Collections
,这时使用 ArrayList 会显得更加便捷:
using namespace System.Collections; [ArrayList]@(1,2,3,4,5,6);
2.2 ArrayList 的常用属性和方法
名称 | 类型 | 描述 |
Capacity |
属性 | 获取或设置 ArrayList 可包含的元素数。 |
Count |
属性 | 获取 ArrayList 中实际包含的元素数。 |
IsFixedSize |
属性 | 获取一个值,该值指示 ArrayList 是否具有固定大小。 |
IsReadOnly |
属性 | 获取一个值,该值指示 ArrayList 是否为只读。 |
IsSynchronized |
属性 | 获取一个值,该值指示是否同步对 ArrayList 的访问(线程安全)。 |
Item[Int32] |
属性 | 获取或设置指定索引处的元素。 |
SyncRoot |
属性 | 获取可用于同步对 ArrayList 的访问的对象。 |
Adapter(IList) | 方法 | 为特定 IList 创建 ArrayList 包装。 |
Add(Object) | 方法 | 将对象添加到 ArrayList 的结尾处。 |
AddRange(ICollection) | 方法 | 将 ICollection 的元素添加到 ArrayList 的末尾。 |
BinarySearch(Int32, Int32, Object, IComparer) | 方法 | 使用指定的比较器在已排序 ArrayList 的某个元素范围中搜索元素,并返回该元素从零开始的索引。 |
BinarySearch(Object) | 方法 | 使用默认的比较器在整个已排序的 ArrayList 中搜索元素,并返回该元素从零开始的索引。 |
BinarySearch(Object, IComparer) | 方法 | 使用指定的比较器在整个已排序的 ArrayList 中搜索元素,并返回该元素从零开始的索引。 |
Clear() | 方法 | 从 ArrayList 中移除所有元素。 |
Clone() | 方法 | 创建 ArrayList 的浅表副本。 |
Contains(Object) | 方法 | 确定某元素是否在 ArrayList 中。 |
CopyTo(Array) | 方法 | 从目标数组的开头开始,将整个 ArrayList 复制到兼容的一维 Array。 |
CopyTo(Array, Int32) | 方法 | 从目标数组的指定索引处开始将整个 ArrayList 复制到兼容的一维 Array。 |
CopyTo(Int32, Array, Int32, Int32) | 方法 | 从目标数组的指定索引处开始,将 ArrayList 中某个范围的元素复制到兼容的一维数组 Array。 |
Equals(Object) | 方法 | 确定指定对象是否等于当前对象。 |
FixedSize(ArrayList) | 方法 | (继承自 Object) 返回具有固定大小的 ArrayList 包装。 |
FixedSize(IList) | 方法 | 返回具有固定大小的 IList 包装。 |
GetEnumerator() | 方法 | 返回用于整个 ArrayList 的枚举数。 |
GetEnumerator(Int32, Int32) | 方法 | 返回 ArrayList 中元素范围的枚举器。 |
GetHashCode() | 方法 | 作为默认哈希函数。 |
GetRange(Int32, Int32) | 方法 | (继承自 Object)返回一个 ArrayList,它表示源 ArrayList 中的元素子集。 |
GetType() | 方法 | 获取当前实例的 Type。 |
IndexOf(Object) | 方法 | (继承自 Object)搜索指定的 Object,并返回整个 ArrayList 中第一个匹配项的从零开始的索引。 |
IndexOf(Object, Int32) | 方法 | 搜索指定的 Object,并返回 ArrayList 中从指定索引到最后一个元素的元素范围中第一个匹配项的从零开始索引。 |
IndexOf(Object, Int32, Int32) | 方法 | 搜索指定的 Object,并返回 ArrayList 中从指定索引开始,并包含指定元素数的元素范围中第一个匹配项的从零开始的索引。 |
Insert(Int32, Object) | 方法 | 将元素插入 ArrayList 的指定索引处。 |
InsertRange(Int32, ICollection) | 方法 | 将集合中的元素插入 ArrayList 的指定索引处。 |
LastIndexOf(Object) | 方法 | 在整个 ArrayList 中搜索指定的 Object,并返回最后一个匹配项的从零开始的索引。 |
LastIndexOf(Object, Int32) | 方法 | 搜索指定的 Object,并返回 ArrayList 中从第一个元素到指定索引这部分元素中最后一个匹配项的从零开始索引。 |
LastIndexOf(Object, Int32, Int32) | 方法 | 搜索指定的 Object,并返回 ArrayList 中到指定索引为止包含指定元素数的这部分元素中最后一个匹配项的从零开始的索引。 |
MemberwiseClone() | 方法 | 创建当前 Object 的浅表副本。 |
ReadOnly(ArrayList) | 方法 | (继承自 Object)返回只读的 ArrayList 包装。 |
ReadOnly(IList) | 方法 | 返回只读的 IList 包装。 |
Remove(Object) | 方法 | 从 ArrayList 中移除特定对象的第一个匹配项。 |
RemoveAt(Int32) | 方法 | 移除 ArrayList 的指定索引处的元素。 |
RemoveRange(Int32, Int32) | 方法 | 从 ArrayList 中移除一系列元素。 |
Repeat(Object, Int32) | 方法 | 返回 ArrayList,其元素是指定值的副本。 |
Reverse() | 方法 | 将整个 ArrayList 中元素的顺序反转。 |
Reverse(Int32, Int32) | 方法 | 将指定范围中元素的顺序反转。 |
SetRange(Int32, ICollection) | 方法 | 复制 ArrayList 中一个子集合的元素。 |
Sort() | 方法 | 对整个 ArrayList 中的元素进行排序。 |
Sort(IComparer) | 方法 | 使用指定的比较器对整个 ArrayList 中的元素进行排序。 |
Sort(Int32, Int32, IComparer) | 方法 | 使用指定的比较器对 ArrayList 中某个范围内的元素进行排序。 |
Synchronized(ArrayList) | 方法 | 返回同步的(线程安全)ArrayList 包装器。 |
Synchronized(IList) | 方法 | 返回同步的(线程安全)IList 包装器。 |
ToArray() | 方法 | 将 ArrayList 的元素复制到新 Object 数组中。 |
ToArray(Type) | 方法 | 将 ArrayList 的元素复制到新的指定元素类型数组中。 |
ToString() | 方法 | 返回表示当前对象的字符串。 |
TrimToSize() | 方法 | (继承自 Object) 将容量设置为 ArrayList 中元素的实际数目。 |
2.3 例子
2.3.1 向 ArrayList 尾插元素
使用Add()
方法可以向 ArrayList 尾插元素,例如:
using namespace System.Collections; $a = [ArrayList]@(); for($i=0; $i -lt 3; $i++){ $a.Add($i); } $a
Out[]:
0 1 2
2.3.2 替换 ArrayList 中的元素
using namespace System.Collections; $a = [ArrayList]@(1,2,3,4,5,6); $a[3] = 9; $a;
Out[]:
1 2 3 9 5 6
2.3.3 移除 ArrayList 中的元素
using namespace System.Collections;
2.3.4 索引 ArrayList 中的元素
using namespace System.Collections;
2.3.5 反转 ArrayList 中的元素
ArrayList 提供了 Reverse()
方法进行就地翻转。例如:
using namespace System.Collections; $a = [ArrayList]@(1,2,3,4,5,6); $a.Reverse(); $a
Out[]:
6 5 4 3 2 1
如果你不希望就地反转,而是生成一个副本,可以参考如下的方式:
using namespace System.Collections; $a = [ArrayList]@(1,2,3,4,5,6); $b = $a.Clone(); $b.Reverse(); $b
Out[]:
6 5 4 3 2 1
这样就不会改变变量 $a 的值。
2.3.6 对 ArrayList 切片
using namespace System.Collections;
2.3.7 按照元素值返回索引
和 Powershell 数组的索引方式一样,如:
using namespace System.Collections; [ArrayList]$a = @(0,1,2,3,4); $a[2..3];
Out[]:
2 3
2.3.8 在指定位置插入元素
using namespace System.Collections;
2.3.9 清空 ArrayList
using namespace System.Collections;
2.3.10 对 ArrayList 元素进行排序
using namespace System.Collections;
3. 哈希表
3.1 哈希表的特点
有时我们需要哈希表来完成更多的功能,这是一种由若干键值对构建而成的数据结构。在一个哈希表中,没有重复的键,但不同键对应的值是可以相同的。其他语言中提供的 字典(Dict)、映射(Map) 都是哈希表。(注意:数据结构领域英文 Map 翻译过来是映射,而非初中英语单词 “地图”)。
3.2 Powershell 哈希表的用法
3.2.1 哈希表的创建
哈希表的创建语法和数组很像,只是使用@{}
(数组是@()
)。例如:
$a = @{ aa = 1; bb = 2; cc = "3"; }; $a
Out[]:
Name Value ---- ----- bb 2 aa 1 cc 3
3.2.2 访问哈希表
哈希表的访问就像查字典,在有些编程语言里面提供的散列表(哈希表)结构的原生数据容器也因此就叫字典(dict)。访问哈希表即访问其中的元素,是通过 键(key) 来获取其中的 值(value) 的。例如:
$a = @{ aa = 1; bb = 2; cc = "3"; }; $a.bb;
Out[]:
2
3.2.3 为哈希表添加键值对
你可以直接为一个字典键赋一个值,以此来新增字符串。例如:
$a = @{ aa = 1; bb = 2; cc = "3"; }; $a.dd = 4; # 添加一个键值对 {dd = 4} $a
Out[]:
Name Value ---- ----- dd 4 cc 3 bb 2 aa 1
3.2.4 修改哈希表指定键的值
修改哈希表的方法与哈希表新增键值对的方法完全一样,只不过是当指定的键不纯在时则为新增键值对,若已经存在则为修改键值对。例如:
$a = @{ aa = 1; bb = 2; cc = "3"; }; $a.aa = 'a'; $a;
Out[]:
Name Value ---- ----- cc 3 bb 2 aa a
3.2.5 删除哈希表中的键值对
使用 Remove(key)
方法可以删除哈希表中的键值对,例如:
$a = @{ aa = 1; bb = 2; cc = "3"; }; $a.Remove('aa'); $a;
Out[]:
Name Value ---- ----- cc 3 bb 2
可以看到,键值对 { aa = 1} 顺利从该哈希表中删除。
3.3 将两个数组映射为 Powershell 哈希表
假设有两个长度相等的数组,一个数组中的所有元素用作键,另一个数组中的所有元素用作值,键和值在两个数组中的位序时意义对应的,以此生成一个哈希表,这时在编程中很使用的功能。以下是实现上述目标的一个参考方法:
function zip { param ( [System.Array]$ary1, [System.Array]$ary2 ) $length1 = $ary1.Count; $length2 = $ary2.Count; $res = @{}; if($length1 -ne $length2){ Write-Warning "两个数组的长度必须一致" }elseif ($length1 -eq 0) { return $res; }else{ for ($i = 0; $i -lt $length1; $i++) { $key = $ary1[$i]; $res.$key = $ary2[$i]; } return $res; } }
调用方法如:
$x=@('a','b','c'); $y = @(1,2,3); zip -ary1 $x -ary2 $y;
Out[]:
Name Value ---- ----- c 3 a 1 b 2