为什么要在PowerShell中使用有序哈希?

时间:2018-10-18 13:44:45

标签: powershell

我正在阅读教程,并了解到PowerShell支持有序哈希。我什么时候使用该功能?

我正在谈论的示例:

$hash = [ordered]@{ ID = 1; Shape = "Square"; Color = "Blue"}

2 个答案:

答案 0 :(得分:9)

使用insert词典的原因是出于显示/打字的目的。例如,如果您要将ordered强制转换为PSCustomObject,并且希望您的键按输入顺序排列,请使用hashtable

这里的用例是当您使用Export-Csv时,标题顺序正确。这只是我想到的一个例子。根据设计,ordered类型与输入键/值的顺序无关,每次显示到成功流中时,顺序都会有所不同。

hashtable字典的另一个用例:您可以将哈希表视为数组,并使用数字访问器查找项目,例如ordered将抢占添加到字典中的最后一个项目。

答案 1 :(得分:6)

让我以更广阔的视角补充TheIncorrigible1's helpful answer

tl; dr

您使用[ordered]支付的性能损失可以忽略不计。


一些背景:

出于技术原因,哈希表(哈希表)的最有效实现让条目的顺序成为实现的结果详细信息不保证向呼叫者的特定订单

这对于用例来说,您要做的就是按键执行孤立的查找,而键(条目)之间的顺序无关紧要。

但是,您经常会关心条目的顺序:

  • 在最简单的情况下,出于 display 的目的;看到定义顺序混乱,有些令人不安;例如:

    @{ 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的哈希表文字语法@{ ... }不是默认为 [ordered] [1] ,但是改变这一点为时已晚。

在一种情况下,其中隐含[ordered] ,但是:如果将哈希表 literal 强制转换为{{1 }}来创建自定义对象:

[pscustomobject][pscustomobject] @{ ... } 语法糖;也就是说,根据哈希表文字中的输入顺序对生成的自定义对象的属性进行排序;例如:

[pscustomobject] [ordered] @{ ... }

但是,请注意,此仅与上述完全相同:如果强制转换直接将 应用于哈希表 literal ;如果您先使用变量将哈希表存储在其中,或者什至只将文字括在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 只能应用于哈希表 literal ;您不能使用它将预先存在的常规哈希表转换为有序哈希表(无论如何,这毫无用处,因为您没有定义的开头):

[ordered]

附带说明:有序哈希表和常规哈希表在通过管道发送时都不会枚举其条目;它们作为整体发送。
要枚举条目,请使用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 对性能的影响:

如前所述,它可以忽略;以下是一些使用Time-Command的示例计时,平均运行10,000次:

[ordered]

示例计时(Windows 10上的Windows PowerShell 5.1,单核VM):

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} }

也就是说,Command TimeSpan Factor ------- -------- ------ $ht=@{one=1;two=2;th... 00:00:00.0000501 1.00 $ht=[ordered] @{one=... 00:00:00.0000527 1.05 仅降低了5%。


[1] TheIncorrigible1指出了一个专门针对[ordered]哈希表的精巧方面

使用 数字,区分索引会变得很棘手; 要强制将数字解释为,请将其强制转换为[ordered]

[object]

也就是说,数字键并不常见,对我来说,默认使用可预测的枚举的好处要胜过这个小问题。

自v1以来,基于# Ordered hashtable with numeric keys. PS> $oht = [ordered] @{ 1 = 'one'; 2 = 'two' } PS> $oht[1] # same as $oht.1 - interpreted as *index* -> 2nd entry two PS> $oht[[object] 1] # interpreted as *key* -> 1st entry. one [ordered]的.NET框架类型已经可用,因此PowerShell可以从一开始就选择它作为System.Collections.Specialized.OrderedDictionary的默认实现,甚至在PowerShell v1中。
鉴于PowerShell致力于向后兼容,但是更改默认值不再是一个选择,因为那样可能会破坏现有代码。