哈希表会加快速度吗?如果是这样,我该怎么做?

时间:2015-12-22 14:18:48

标签: arrays powershell dictionary

我正在寻找两个数组的负面交集。每个阵列都有大约20k个元素。我在一个数组上使用foreach循环,并在另一个数组中查找每个值。我只保留第二个数组中找不到的第一个数组中的元素:

$deadpaths=@()
$ix=0
ForEach ($f in $FSBuildIDs)
{
    if (-not($blArray -like $f)) {$deadpaths+=$paths[$ix]}
    $ix++
}

$ blArray包含有效的ID。 $ FSBuildIDs包含与$ paths中的文件系统路径对应的ID。目的是仅将$ FSBuildIDS中相应ID的$ path中的元素保留在$ blArray中。

有更好的方法吗?这里的处理需要很长时间。 $ blArray和$ FSBuildID都有大约20k元素,我怀疑我正在看On ^ 2比较。

我想过使用带有$ FSBuildIDs元素的Dictionary作为键和$ path作为值,但是我无法从文档中找出如何初始化和加载Dictionary(假设这种方法可以加快速度) )。显然负设置交叉点是最好的,但这不是TSQL,我很痛苦地意识到即使PS的V4也不支持设置操作。

在这个问题中使用字典会加快比较吗?如果是这样,我如何从$ FSBuildIDs和$ paths创建它?任何其他技术可能会给我带来性能提升而只是迭代这些大(ish)列表?

$ blArray的示例数据:

51012
51044
51049
51055
51058
51060
51073
51074
51077
51085

$ FSBuildIDs的示例数据:

51001
51003
51005
51009
51013
51017
51018
51020
51021
51024
51026

$ path的示例数据:

\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2335
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2336
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2337
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2338
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2339
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2340
\\server1\d$\software\anthill\var\artifacts\0000\3774\0000\3792\0005\2341

这类似于先前提出的问题,但在某些方面有所不同。我基本上是在寻找从两个现有数组构建字典的指导。我在发帖后意识到我真的需要一个来自$ blarray的字典作为键,并且可能是$ True作为值。价值无关紧要。重要的测试是$ $ BizBuildIDs中的当前值是否在$ blarray中找到。这可以是基于ID作为关键字的字典查找。这应该加快处理速度,对吧?

我不清楚我每次都在摧毁和重新创建数组的评论。那是$ deadPaths数组吗?只需添加它就会导致这种情况?如果是这样我会更好地使用.Net ArrayList?

3 个答案:

答案 0 :(得分:0)

我认为这将是您正在寻找的开始。正如评论中所讨论的,我们将进行两次比较。首先要获取BuildID,我们需要从$FSBuildIDs$blArray进行比较,然后我们将结果与$paths列表进行比较。我将假设它现在只是一个字符串数组路径。请注意,此处存在错误预防和纠正的余地。现在还在测试。

$parsedIDs = Compare-Object $blArray $FSBuildIDs | Where{$_.SideIndicator -eq "=>"} | Select-Object -ExpandProperty InputObject

$paths = $paths | ForEach-Object{
    $_ | Add-Member -MemberType NoteProperty -Name BuildID -Value (($_.Parent.Name + $_.Name) -as [int32]) -PassThru
}

$paths | Where-Object{$_.BuildID -in $parsedIDs}

首先,我们比较两个ID数组并保留$FSBuildIDs的唯一元素。

接下来我们浏览$paths。对于每一个,我们添加一个包含buildid的属性。其中buildid是连接的最后两个路径元素并转换为整数。

一旦我们得到一个简单的Where-Object给我们第一次比较中存在id的路径。

答案 1 :(得分:0)

使用-contains运算符代替-like可以实现显着改进。

-like操作的左侧是一个数组时,PowerShell将迭代该数组并对每个条目执行-like比较。

另一方面,

-contains会在找到匹配后立即返回。

考虑以下示例:

$array1 = 1..2000
$array2 = 2..2001

$like = Measure-Command {
    foreach($i in $array2){
        $array1 -like $i
    }
} |Select -Expand TotalMilliseconds

$contains = Measure-Command {
    foreach($i in $array2){
        $array1 -contains $i
    }
} |Select -Expand TotalMilliseconds

Write-Host "Operation with -like     took: $($like)ms"
Write-Host "Operation with -contains took: $($contains)ms"

就像在你的真实世界的例子中一样,我们有2个具有大重叠的整数数组。让我们看看它在我的Windows 7笔记本电脑(PowerShell 4.0)上的表现如何:

numpy.array(x)

我认为结果不言自明: - )

话虽如此,正如您所预料的那样,您可以通过填充哈希表来实现更大的改进,使用第一个数组中的值作为键:

$hashtable = $array1 |ForEach-Object -Begin {$t = @{}} -Process {
    $t[$_] = $null 
    # the value doesn't matter, we're only interested in the key lookup
} -End { $t }

然后在哈希表上使用ContainsKey()方法而不是-like

foreach($i in $array2){
    if($hashtable.ContainsKey($i)) { # do stuff }
}

您需要提高数组的大小以查看实际差异(此处使用第一个数组中的20K项目):

enter image description here

最终测试脚本enter image description here

答案 2 :(得分:0)

回答有关构建哈希表的问题:

$keyEnumerator = $FSBuildIDs.GetEnumerator()
$valEnumerator = $paths.GetEnumerator()
$idPathHash = @{}

foreach ($key in $keyEnumerator ) {
  $null = $valEnumerator.movenext()
  $idPathHash[$key] = $valEnumerator.current
}

在我的系统上使用20000个元素的假数据数组运行此代码需要138毫秒。

构建不在$ idPathHash中的构建ID列表:

$buildIDsNotIn =
  foreach ($buildId in $blArray) {
    if (!$idPathHash.ContainsKey($buildId )) {
      $buildId 
    } 
  }

我的系统花了50毫秒,$ blArray中有20000个项目,还有假数据。