我想在PowerShell中创建一个2元素(对)元组的哈希集。
$MySet = New-Object System.Collections.Generic.HashSet[System.Tuple]
哪一个 看来 来工作。
$MySet.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True HashSet`1 System.Object
$MySet.Add
OverloadDefinitions
-------------------
bool Add(System.Tuple item)
void ICollection[Tuple].Add(System.Tuple item)
bool ISet[Tuple].Add(System.Tuple item)
但如果我创建一个元组并将其添加到集合中,
$Tuple = [System.Tuple]::Create("Test", "Hello")
$MySet.Add($Tuple)
MethodException: Cannot find an overload for "Add" and the argument count: "1".
有什么方法可以正确地进行呢?
奖励:没有 哈希集的线程安全版本 - 有什么方法可以以线程安全的方式修改read哈希集?
这里的问题是 [System.Tuple]::Create($a,$b)
不 实际上创建了一个 [System.Tuple]
但是 [System.Tuple[T1,T2]]
哪儿 T1
是一种 $a
和 T2
是一种 $b
.
虽然它们共用部分名称。[System.Tuple[T1,T2]]
是 不可转让 到 [System.Tuple]
所以,我们必须为你的 [HashSet]
.
因为你的元组项目值是字符串,所以选择了 [System.Tuple[string,string]]
:
$set = [System.Collections.Generic.HashSet[System.Tuple[string,string]]]::new()
$tuple = [System.Tuple]::Create("Hello", "World")
$set.Add($tuple)
如果你走另一个方向,只是创建一个 HashSet[object]
为了实现尽可能广泛的可分配性,你可以创建一个线程安全的 ConcurrentSet
包裹 HashSet[object]
方法,然后在自定义的 powershell 类中使用 ReaderWriteLockSlim
以方便并发读而独占写。
using namespace System.Collections.Concurrent
using namespace System.Collections.Generic
using namespace System.Threading
# Custom IEqualityComparer based on [scriptblock]
# Use [PSComparer]::new({$args[0].Equals($args[1])}) to "restore" default comparer logic
class PSComparer : IEqualityComparer[object]
{
[scriptblock]
$Comparer
PSComparer()
{
$this.Comparer = {$args[0] -eq $args[1]}
}
PSComparer([scriptblock]$comparer)
{
$this.Comparer = $comparer
}
[bool]
Equals($a,$b)
{
return & $this.Comparer $a $b
}
[int]
GetHashCode($obj)
{
if($obj -is [object]){
return $obj.GetHashCode()
}
throw [System.ArgumentNullException]::new('obj')
}
}
class ConcurrentSet : IDisposable
{
hidden [ReaderWriterLockSlim]
$_lock
hidden [HashSet[object]]
$_set
ConcurrentSet()
{
# Default to PowerShell comparison logic, ie. `"1" -eq 1`
$this.Initialize([PSComparer]::new())
}
ConcurrentSet([IEqualityComparer[object]]$comparer)
{
$this.Initialize($comparer)
}
hidden
Initialize([IEqualityComparer[object]]$comparer)
{
$this._set = [HashSet[object]]::new($comparer)
$this._lock = [System.Threading.ReaderWriterLockSlim]::new()
}
[bool]
Add([object]$item)
{
$this._lock.EnterWriteLock()
try{
return $this._set.Add($item)
}
finally{
$this._lock.ExitWriteLock()
}
}
[bool]
Contains([object]$item)
{
$this._lock.EnterReadLock()
try{
return $this._set.Contains($item)
}
finally{
$this._lock.ExitReadLock()
}
}
[bool]
Remove([object]$item)
{
$this._lock.EnterUpgradeableReadLock()
try{
if($this._set.Contains($item)){
$this._lock.EnterWriteLock()
try {
return $this._set.Remove($item)
}
finally {
$this._lock.ExitWriteLock()
}
}
return $false
}
finally{
$this._lock.ExitUpgradeableReadLock()
}
}
UnionWith([IEnumerable[object]]$other)
{
$this._lock.EnterWriteLock()
try{
$this._set.UnionWith($other)
}
finally{
$this._lock.ExitWriteLock()
}
}
IntersectWith([IEnumerable[object]]$other)
{
$this._lock.EnterWriteLock()
try{
$this._set.IntersectWith($other)
}
finally{
$this._lock.ExitWriteLock()
}
}
ExceptWith([IEnumerable[object]]$other)
{
$this._lock.EnterWriteLock()
try{
$this._set.ExceptWith($other)
}
finally{
$this._lock.ExitWriteLock()
}
}
SymmetricExceptWith([IEnumerable[object]]$other)
{
$this._lock.EnterWriteLock()
try{
$this._set.SymmetricExceptWith($other)
}
finally{
$this._lock.ExitWriteLock()
}
}
[bool]
IsSubsetOf([IEnumerable[object]]$other)
{
$this._lock.EnterReadLock()
try{
return $this._set.IsSubsetOf($other)
}
finally{
$this._lock.ExitReadLock()
}
}
[bool]
IsSupersetOf([IEnumerable[object]]$other)
{
$this._lock.EnterReadLock()
try{
return $this._set.IsSupersetOf($other)
}
finally{
$this._lock.ExitReadLock()
}
}
[bool]
IsProperSubsetOf([IEnumerable[object]]$other)
{
$this._lock.EnterReadLock()
try{
return $this._set.IsProperSubsetOf($other)
}
finally{
$this._lock.ExitReadLock()
}
}
[bool]
IsProperSupersetOf([IEnumerable[object]]$other)
{
$this._lock.EnterReadLock()
try{
return $this._set.IsProperSupersetOf($other)
}
finally{
$this._lock.ExitReadLock()
}
}
[bool]
Overlaps([IEnumerable[object]]$other)
{
$this._lock.EnterReadLock()
try{
return $this._set.Overlaps($other)
}
finally{
$this._lock.ExitReadLock()
}
}
[bool]
SetEquals([IEnumerable[object]]$other)
{
$this._lock.EnterReadLock()
try{
return $this._set.SetEquals($other)
}
finally{
$this._lock.ExitReadLock()
}
}
hidden [int]
get_Count()
{
return $this._set.Count
}
Dispose()
{
if($this._lock -is [System.IDisposable])
{
$this._lock.Dispose()
}
}
}
另一个(丑陋)的方法是
$MySet = [System.Collections.Generic.HashSet[[System.Tuple[string,string]]]]::new()
$Tuple = [System.Tuple[string,string]]::new("Test", "Hello")
[void]$MySet.Add($Tuple)
$MySet
结果:项目1 项目2 长度
项目1 项目2 长度----------------------------------------------------------------------------------------------------------------------测试 你好 2