PowerShell 比较运算符包含与大型数组



想知道在数组搜索之后是否有机会提高性能。目前是16秒。

Measure-Command -Expression {
$a = @()
$b = @()
1..10000 | %{$a += $_}
1..10000 | %{$b += $_}
#Try to resize but still running 16 seconds
[array]::Resize([ref]$a,10000) 
[array]::Resize([ref]$b,10000)
foreach ($i in $a){
if ($b -contains $i) {
#write-host $i
}
}

}

+=
  1. 数组的情况下重新创建整个数组:它将旧内容与新元素一起复制到新数组中。这可以说是在PowerShell中填充大型数组的最糟糕的方法。仅当+=是不在循环内的一次性操作,或者数组很小且时间无关紧要时,才使用 。

    直接填充数组:

    $a = 1..10000
    

    或者直接收集foreach语句的输出:

    $a = foreach ($i in 1..10000) { $i }
    

    或者使用 ArrayList:

    $a = [Collections.ArrayList]@()
    foreach ($i in 1..10000) { $a.Add($i) >$null }
    

    请注意,在这种特殊情况下,将现有数组转换为 ArrayList 会更快:

    $a = [Collections.ArrayList]@(1..10000)
    

     

  2. 水线是一个复杂的操作,它比流控制语句(如foreach(语句,而不是 cmdlet)、whiledo等流控制语句慢几倍/很多倍。

     

  3. 与内部的简单代码相比,ScriptBlock(大括号中的代码{ }cmdletForEach中的代码,别名为%)需要花费大量时间来为每个元素创建执行上下文。

     

  4. -contains检查每个元素,直到找到匹配项,因此最终的迭代次数在最坏的情况下$a.count * $b.count或平均的一半。

     

最有效的方法是构建查找表:

  • 作为哈希表:

    $a = 1..10000
    $b = 1..10000
    $bIndex = @{}
    foreach ($i in $b) { $bIndex[$i] = $true }
    foreach ($i in $a) {
    if ($bIndex[$i]) {
    #write-host $i
    }
    }
    

    在 i7 CPU 上为 15 毫秒

  • 作为哈希集(.NET 3.5 及更高版本,自 Win7 起内置,可安装在 XP 上):

    $a = 1..10000
    $b = 1..10000
    $bIndex = [Collections.Generic.HashSet[int]]$b
    foreach ($i in $a) {
    if ($bIndex.Contains($i)) {
    #write-host $i
    }
    }
    

    在 i7 CPU 上为 7 毫秒

  • 使用 HashSet 与数组相交,然后迭代结果:

    $a = 1..10000
    $b = 1..10000
    $inBoth = [Collections.Generic.HashSet[int]]$a
    $inBoth.IntersectWith([Collections.Generic.HashSet[int]]$b)
    foreach ($i in $inBoth) {
    #write-host $i
    }
    

    在 i7 CPU 上为 7 毫秒

    如果数组的内容不同,即当交集比数组小得多时,它会快很多倍。

也可以构建一个真正的索引,其中包含数组中原始项目索引的列表 - 在重复值的情况下很有用。

最新更新