powershell 比较运算符包含 vs 大数组

powershell comparison operator contain vs large array

想知道是否有机会提高以下数组搜索的性能。现在是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)、while、[=22 等流控制语句慢 several/many 倍=].

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

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

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

  • 作为散列table:

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

    15 milliseconds on i7 CPU

  • 作为 HashSet(.NET 3.5 及更新版本,built-in since 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
        }
    }
    

    7 milliseconds on i7 CPU

  • 使用 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
    }
    

    7 milliseconds on i7 CPU

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

也可以在数组中构建一个真正的 - 在重复值的情况下很有用。