powershell - 为什么要在 PowerShell 中使用有序哈希?
问题描述
我正在阅读教程并了解到 PowerShell 支持有序哈希。我什么时候会使用该功能?
我正在谈论的示例代码:
$hash = [ordered]@{ ID = 1; Shape = "Square"; Color = "Blue"}
解决方案
让我用更广阔的视角补充Maximilian Burszley 的有用答案:
tl;博士
大多数时候你想要
[ordered] @{ ... }
([System.Collections.Specialized.OrderedDictionary]
)(PSv3+):- 它按照定义的顺序提供条目的枚举(也反映在
.Keys
和.Values
集合属性中)。 - 它还允许通过索引访问条目,例如数组。
- 它按照定义的顺序提供条目的枚举(也反映在
通常,您可以与常规哈希表aka
[ordered] @{ ... }
互换使用,因为这两种类型都实现了interface,这就是接受哈希表的参数的典型输入方式。@{ ... }
[hashtable]
[System.Collections.Hashtable]
[IDictionary]
您为使用而付出的性能损失可以[ordered]
忽略不计。
一些背景:
由于技术原因,哈希表(哈希表)最有效的实现是让条目的顺序成为实现细节的结果,而不保证调用者的任何特定顺序。
这适用于您所做的只是通过键执行隔离查找的用例,其中键(条目)之间的顺序无关紧要。
但是,您通常确实关心条目的顺序:
在最简单的情况下,用于显示目的;看到定义顺序混乱有些令人不安;例如:
@{ one = 1; two = 2; three = 3 } Name Value ---- ----- one 1 three 3 # !! two 2
更重要的是,条目的枚举可能需要可预测以进行进一步的程序化处理;例如(注意:严格来说,属性顺序在 JSON 中并不重要,但对于人类观察者来说又很重要):
# No guaranteed property order. PS> @{ one = 1; two = 2; three = 3 } | ConvertTo-Json { "one": 1, "three": 3, # !! "two": 2 } # Guaranteed property order. PS> [ordered] @{ one = 1; two = 2; three = 3 } | ConvertTo-Json { "one": 1, "two": 2, "three": 3 }
不幸的是,PowerShell 的 hashtable-literal 语法@{ ... }
不默认为 [ordered]
[1],但改变它为时已晚。
但是,有一个上下文[ordered]
是隐含的:如果您将哈希表文字转换[pscustomobject]
为以创建自定义对象:
[pscustomobject] @{ ... }
是[pscustomobject] [ordered] @{ ... }
;的语法糖 也就是说,生成的自定义对象的属性根据哈希表文字中的条目顺序进行排序;例如:
PS> [pscustomobject] @{ one = 1; two = 2; three = 3 }
one two three # order preserved!
--- --- -----
1 2 3
但是请注意,这只适用于如上所示:如果强制转换直接应用于哈希表文字;如果您首先使用变量存储哈希表,或者您甚至只是将文字包含在(...)
排序中,则会丢失:
PS> $ht = @{ one = 1; two = 2; three = 3 }; [pscustomobject] $ht
one three two # !! Order not preserved.
--- ----- ---
1 3 2
PS> [pscustomobject] (@{ one = 1; two = 2; three = 3 }) # Note the (...)
one three two # !! Order not preserved.
--- ----- ---
1 3 2
因此,如果您先迭代地构造一个哈希表,然后将其强制转换为[pscustomobject]
,则必须从[ordered]
哈希表开始以获得可预测的属性排序;这种技术很有用,因为创建哈希表条目比向自定义对象添加属性更容易;例如:
$oht = [ordered] @{} # Start with an empty *ordered* hashtable
# Add entries iteratively.
$i = 0
foreach ($name in 'one', 'two', 'three') {
$oht[$name] = ++$i
}
[pscustomobject] $oht # Convert the ordered hashtable to a custom object
最后,注意[ordered]
只能应用于 hashtable literal;您不能使用它将预先存在的常规哈希表转换为有序哈希表(无论如何这都没有任何意义,因为您一开始没有定义的顺序):
PS> $ht = @{ one = 1; two = 2; three = 3 }; [ordered] $ht # !! Error
...
The ordered attribute can be specified only on a hash literal node.
...
附带说明:通过管道发送时,有序哈希表和常规哈希表都不会枚举它们的条目;它们是作为一个整体发送的。
要枚举条目,请使用.GetEnumerator()
方法;例如:
@{ one = 1; two = 2; three = 3 }.GetEnumerator() | ForEach-Object { $_.Value }
1
3 # !!
2
至于使用的性能影响[ordered]
:
如前所述,它可以忽略不计;以下是一些样本计时,平均 10,000 次运行,使用Time-Command
:
Time-Command -Count 10,000 { $ht=@{one=1;two=2;three=3;four=4;five=5;six=6;seven=7;eight=8;nine=9}; foreach($k in $ht.Keys){$ht.$k} },
{ $ht=[ordered] @{one=1;two=2;three=3;four=4;five=5;six=6;seven=7;eight=8;nine=9}; foreach($k in $ht.Keys){$ht.$k} }
示例计时(Windows 10 上的 Windows PowerShell 5.1,单核 VM):
Command TimeSpan Factor
------- -------- ------
$ht=@{one=1;two=2;th... 00:00:00.0000501 1.00
$ht=[ordered] @{one=... 00:00:00.0000527 1.05
也就是说,[ordered]
仅放缓了 5%。
[1] Maximilian Burszley 指出了哈希表特有的一个棘手方面[ordered]
:
使用数字键,区分键和索引会变得很棘手;要强制将数字解释为键,请将其强制转换为[object]
或使用点表示法(.
属性访问语法)而不是索引语法([...]
):
# Ordered hashtable with numeric keys.
PS> $oht = [ordered] @{ 1 = 'one'; 2 = 'two' }
PS> $oht[1] # interpreted as *index* -> 2nd entry
two
PS> $oht[[object] 1] # interpreted as *key* -> 1st entry.
one
PS> $oht.1 # dot notation - interpreted as *key* -> 1st entry.
one
也就是说,数字键并不常见,对我来说,默认为可预测枚举的好处超过了这个小问题。
.NET 类型底层[ordered]
, System.Collections.Specialized.OrderedDictionary
, 从 v1 开始就可用,因此 PowerShell 可以@{ ... }
从一开始就选择它作为默认实现,即使在 PowerShell v1 中也是如此。
鉴于 PowerShell 对向后兼容性的承诺,更改默认值不再是一种选择,因为这可能会破坏现有代码,即通过以下方式:
可能存在检查无类型参数是否为带有 的哈希表的现有代码
-is [hashtable]
,这将不再适用于有序哈希表(但是,检查带有-is [System.Collections.IDictionary]
将起作用)。可能存在依赖于带有数字键的哈希表的现有代码,在这种情况下,索引语法查找行为会发生变化(参见上面的示例)。
推荐阅读
- django - django - 如何将日期时间字段保存(更新)到数据库
- wordpress - 如何在 Google Instance VM 上启用 FTP/SFTP?
- c# - 如何在字典中排序键?
- java - 如果我更改传递给 Arrays.asList(array) 的数组,列表值会发生变化
- javascript - 通信 PC 和 Android 手机(从浏览器)并从手机到 PC 调用操作
- python - 在python中读取变量列多个标题csv文件
- python - 访问所有节点并将它们添加到路径
- vba - Excel宏比较两个不同工作表中的两列并用序号替换它们的值
- java - 如何将 jdbcTemplate.queryForList 语句作为列表传递
- php - After adding jquery CDN link to my code, contact us "submit" button stop working