Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Powershell 从数组属性获取唯一索引项的最快方法_Powershell - Fatal编程技术网

Powershell 从数组属性获取唯一索引项的最快方法

Powershell 从数组属性获取唯一索引项的最快方法,powershell,Powershell,制作一个这样的数组,表示我要查找的内容: $array = @(1..50000).foreach{[PSCustomObject]@{Index=$PSItem;Property1='Hello!';Property2=(Get-Random)}} 获取索引属性为“43122”的项的最快方法是什么 我有一些想法,但我觉得必须有一个更快的方法: 管道在哪里 何处方法 首先创建一个哈希表并查询“索引”结果。开始时速度慢,但随后的查找速度更快。 有没有比先构建哈希表更快的方法?可能是一种不同的.N

制作一个这样的数组,表示我要查找的内容:

$array = @(1..50000).foreach{[PSCustomObject]@{Index=$PSItem;Property1='Hello!';Property2=(Get-Random)}}
获取索引属性为“43122”的项的最快方法是什么

我有一些想法,但我觉得必须有一个更快的方法:

管道在哪里 何处方法 首先创建一个哈希表并查询“索引”结果。开始时速度慢,但随后的查找速度更快。
有没有比先构建哈希表更快的方法?可能是一种不同的.NET数组类型,比如某种特殊类型的索引列表,我可以先将其存储在其中,然后运行一个方法,根据唯一属性提取项?

我认为最快的方法是使用哈希表,并想当然地认为构建该表需要一些时间。 此外,我将反转哈希表,以便您要查找的属性是键,数组indexd是值

请注意,虽然您的示例创建了一个起始索引为1的数组,但在以后检索确切的索引(从0开始)时需要考虑到这一点。 还请注意,通过使用属性的
(Get Random)
搜索,您可能会得到重复的值。 对于演示来说,这很好,但请记住,执行此操作时,找到的索引将是重复序列中的最后一个索引

# create the demo array of objects
$startIndex = 0
$array = @($startIndex..50000).Foreach{[PSCustomObject]@{Index=$PSItem; Property1='Hello!'; Property2=(Get-Random)}}

# create the hashtable where Property2 is the key and the array index the value
Write-Host 'Create HashTable: ' -NoNewline
(Measure-Command { $ht = @{}; foreach ($i in $array) { $ht[$i.Property2] = ($i.Index - $startIndex) } }).TotalMilliseconds

# try and find the index. This will take longer if there was no Property2 with value 43122 
Write-Host 'Find array index: ' -NoNewline
(Measure-Command { $ht[43122] }).TotalMilliseconds
在我的Windows 7计算机上的输出(12 GB RAM,SSD磁盘):


部分由于PowerShell能够调用方法,它提供了一些过滤对象的可能性。在stackoverflow,您将发现许多(PowerShell)问题和答案,用于衡量特定解救命令或命令的性能。这通常会给人留下错误的印象,因为一个完整的(PowerShell)解决方案的性能应该比其各部分的总和更好。每个命令都取决于预期的输入和输出。尤其是在使用PowerShell管道时,命令(cmdlet)会与之前的命令和随后的命令交互。因此,重要的是要着眼于全局,了解每个命令如何以及在何处获得其性能。
这意味着我不能告诉您应该选择哪个命令,但如果您对下面列出的命令和概念有了更好的理解,我希望您能够更好地为您的特定解决方案找到“最快的方法”

通常(dis)被鉴定为PowerShell中过滤对象的快速解决方案(另请参见):

刚刚超过
4ms
!,其他任何方法都无法击败这一点……
但是,在得出任何结论,即LINQ比任何其他方法高出100倍或更多之前,你应该记住以下几点。当您只看活动本身的性能时,测量LINQ查询的性能有两个陷阱:

  • LINQ有一个大缓存,这意味着您应该重新启动一个新的PowerShell会话来测量实际结果(如果您经常想重用查询,也可以不重启)。重新启动PowerShell会话后,您会发现启动LINQ查询所需的时间将延长约6倍
  • 但更重要的是,LINQ执行(也称为)。这意味着除了定义应该执行的操作之外,实际上还没有执行任何操作。这实际上显示了您是否要访问
    $Result
    的一个属性:

检索单个对象的属性通常需要
15ms

$Item = [PSCustomObject]@{Index=1; Property1='Hello!'; Property2=(Get-Random)}
(Measure-Command {
    $Item.Property1
}).totalmilliseconds
15.3708
总之,您需要实例化结果以正确度量LINQ查询的性能(为此,让我们仅检索度量中返回对象的一个属性):

(速度仍然很快。)

哈希表通常速度很快,因为它们基于最大值,这意味着您必须猜测
ln 50000/ln 2=16次才能找到您的对象。尽管如此,为单个查找构建一个
HashTabe
还是有点过头了。但是,如果您控制对象列表的构造,则可以随时构造哈希表:

(Measure-Command {
    $ht = @{}
    $array = @(1..50000).foreach{$ht[$PSItem] = [PSCustomObject]@{Index=$PSItem;Property1='Hello!';Property2=(Get-Random)}}
    $ht.43122
}).totalmilliseconds
3415.1196
vs:

vs 由于您可能已经得出结论,
Where
方法的显示速度大约是
Where Object
cmdlet的两倍:

Where Object
cmdlet

(Measure-Command {
    $Result = $Array | Where-Object index -eq 43122
}).totalmilliseconds
721.545
(Measure-Command {
    Import-Csv -Path .\Test.csv | Where-Object index -eq 43122 | Export-Csv -Path .\Result.csv
}).totalmilliseconds
717.8306
(Measure-Command {
    $Result = $Array | ForEach-Object {If ($_.index -eq 43122) {$_}}
}).totalmilliseconds
1031.1599
(Measure-Command {
    Import-Csv -Path .\Test.csv |
    ForEach-Object {If ($_.index -eq 43122) {$_}} |
    Export-Csv -Path .\Result.csv
}).totalmilliseconds
1978.4703
其中
方法:

原因是
Where
命令要求将整个数组加载到内存中,而
Where-Object
cmdlet实际上不需要这样做。如果数据已经在内存中(例如,通过将其分配给变量
$array=…
),这并不是什么大问题,但这本身实际上可能是一个缺点:除了它消耗内存之外,您必须等到收到所有对象后才能开始筛选

不要低估PowerShell cmdlet(如
Where Object
)的强大功能,尤其要将解决方案作为一个整体与管道相结合。如上所示,如果您仅对特定操作进行测量,您可能会发现这些cmdlet的速度很慢,但如果您对整个端到端解决方案进行测量,您可能会发现没有太大差异,cmdlet甚至可能优于其他技术。当LINQ查询非常被动时,PowerShell cmdlet非常主动。
通常,如果您的输入尚未在内存中并通过管道提供,则应尝试继续在该管道上构建,并通过避免变量赋值
$array=…
和使用方括号
(…)
)来避免以任何方式暂停输入:

假设您的对象来自较慢的输入,在这种情况下,所有其他解决方案都需要等待最后一个对象能够开始过滤,
where object
已经动态过滤了大多数对象,并且一旦找到它,就会不确定地传递给下一个cmdlet

例如
$Item = [PSCustomObject]@{Index=1; Property1='Hello!'; Property2=(Get-Random)}
(Measure-Command {
    $Item.Property1
}).totalmilliseconds
15.3708
(Measure-Command {
    $Result = ([Linq.Enumerable]::Where($array, [Func[object,bool]] { param($Item); return $Item.Index -eq 43122 })).Property1
}).totalmilliseconds
570.5087
(Measure-Command {
    $ht = @{}
    $array = @(1..50000).foreach{$ht[$PSItem] = [PSCustomObject]@{Index=$PSItem;Property1='Hello!';Property2=(Get-Random)}}
    $ht.43122
}).totalmilliseconds
3415.1196
(Measure-Command {
    $array = @(1..50000).foreach{[PSCustomObject]@{Index=$PSItem;Property1='Hello!';Property2=(Get-Random)}}
    $ht = @{}; $array.foreach{$ht[$PSItem.index] = $psitem}
    $ht.43122
}).totalmilliseconds
3969.6451
(Measure-Command {
    $Result = $Array | Where-Object index -eq 43122
}).totalmilliseconds
721.545
(Measure-Command {
    $Result = $Array.Where{$_ -eq 43122}
}).totalmilliseconds
319.0967
$Array | Export-Csv .\Test.csv
(Measure-Command {
    Import-Csv -Path .\Test.csv | Where-Object index -eq 43122 | Export-Csv -Path .\Result.csv
}).totalmilliseconds
717.8306
(Measure-Command {
    $Array = Import-Csv -Path .\Test.csv
    Export-Csv -Path .\Result.csv -InputObject $Array.Where{$_ -eq 43122}
}).totalmilliseconds
747.3657
(Measure-Command {
    If ($Array.Index -eq 43122) {'Found object with the specific property value'}
}).totalmilliseconds
55.3483
(Measure-Command {
    $Result = $Array | ForEach-Object {If ($_.index -eq 43122) {$_}}
}).totalmilliseconds
1031.1599
(Measure-Command {
    $Result = $Array.ForEach{If ($_.index -eq 43122) {$_}}
}).totalmilliseconds
781.6769
(Measure-Command {
    Import-Csv -Path .\Test.csv |
    ForEach-Object {If ($_.index -eq 43122) {$_}} |
    Export-Csv -Path .\Result.csv
}).totalmilliseconds
1978.4703
(Measure-Command {
    $Array = Import-Csv -Path .\Test.csv
    Export-Csv -Path .\Result.csv -InputObject $Array.ForEach{If ($_.index -eq 43122) {$_}}
}).totalmilliseconds
1447.3628
(Measure-Command {
    $Result = $Null
    ForEach ($Item in $Array) {
        If ($Item.index -eq 43122) {$Result = $Item}
    }
}).totalmilliseconds
382.6731
(Measure-Command {
    $Result = $Null
    $Array = Import-Csv -Path .\Test.csv
    ForEach ($Item in $Array) {
        If ($item.index -eq 43122) {$Result = $Item}
    }
    Export-Csv -Path .\Result.csv -InputObject $Result
}).totalmilliseconds
1078.3495
(Measure-Command {
    $Result = $Null
    ForEach ($Item in $Array) {
        If ($item.index -eq 25000) {$Result = $Item; Break}
    }
}).totalmilliseconds
138.029