我在C#中确实有一个HashSet,如下所示:
HashSet<List<int>> _hash = new HashSet<List<int>>();
现在,我在其中插入了一个值,如下所示:
_hash.add(new List<int> {1,7});
当我在上面的代码之后编写以下代码时:
_hash.contains(new List<int>{1,7});
我期望它返回true,因为刚刚添加了相同的值,但是它确实返回了false。这确实使我感到困惑。此外,如何确保当我具有List的哈希集时,在向其中添加任何新值之前,其中没有重复项。
我认为使用HashSet的全部原因是为了避免任何重复,但似乎这允许重复。
现在,从角度看,我想要的只是当我有一个List>时,如何确保进入List>的每个元素(列表)都是唯一的?
答案 0 :(得分:1)
您可以创建自己的类似只读集合。
public class ComparableReadOnlyCollection<T> : ReadOnlyCollection<T>
{
public ComparableReadOnlyCollection(IList<T> list)
: base(list.ToArray())
{
}
public override bool Equals(object other)
{
return
other is IEnumerable<T> otherEnumerable &&
otherEnumerable.SequenceEqual(this);
}
public override int GetHashCode()
{
int hash = 43;
unchecked {
foreach (T item in this) {
hash = 19 * hash + item.GetHashCode();
}
}
return hash;
}
}
请注意,ReadOnlyCollection<T>
只是原始列表的包装。如果您修改此列表,则ReadOnlyCollection<T>
将反映这些更改。我的实现将原始列表复制到一个数组中,以使其真正不可变。
但是请注意,如果元素T
是引用类型,您仍然可以修改原始对象的成员!所以要小心
此测试按预期进行:
var hashSet = new HashSet<ComparableReadOnlyCollection<int>>();
hashSet.Add(new ComparableReadOnlyCollection<int>(new [] { 1, 7 }));
Console.WriteLine(hashSet.Contains(new ComparableReadOnlyCollection<int>(new [] { 1, 7 })));
Console.WriteLine(hashSet.Contains(new ComparableReadOnlyCollection<int>(new [] { 7, 1 })));
Console.WriteLine(hashSet.Contains(new ComparableReadOnlyCollection<int>(new [] { 1, 7, 0 })));
hashSet.Add(new ComparableReadOnlyCollection<int>(new [] { 1, 7 }));
hashSet.Add(new ComparableReadOnlyCollection<int>(new [] { 1, 7, 0 }));
hashSet.Add(new ComparableReadOnlyCollection<int>(new [] { 7, 1 }));
Console.WriteLine(hashSet.Count);
Console.ReadKey();
它打印
True
False
False
3
请注意,它不会打印4,因为该集中不能有重复项。
第二种解决方法:
阅读您的编辑后,我不确定您真正想要的是什么。您是要创建HashSet<int>
而不是HashSet<List<int>>
并比较列表的元素而不是列表本身吗?
HashSet<int> _hash = new HashSet<int>(new List<int> { 1, 1, 2, 3, 5, 8, 13 });
现在,哈希集包含数字{1,2,3,5,8,13}。设置元素始终是唯一的。
然后您可以测试
var hash2 = new HashSet<int> { 3, 8 };
if (_hash.IsSupersetOf(hash2)) {
Console.WriteLine("_hash contains 3 and 8");
}
或等价的内容:
if (hash2.IsSubsetOf(_hash)) {
Console.WriteLine("_hash contains 3 and 8");
}
第三个解决方案:
List<HashSet<int>>
呢?因为现在,您可以将set操作应用于列表的每个元素(哈希集)。
答案 1 :(得分:-1)
问题
在HashSet中添加列表时,您会添加一个新引用,因此从HashSet的角度来看,没有重复项。
Contains
方法仅比较引用,因此在您的示例中有两个不同的引用。
您需要检查自己是否包含类似的列表。
使用扩展方法
using System.Collections.Generic;
static public class HashSetHelper
{
static public bool ContainsSequence(this HashSet<List<int>> set, List<int> list)
{
if ( set == null || set.Count == 0 )
return false;
if ( list == null )
return set.Contains(null);
foreach ( var item in set )
{
if ( item.Count != list.Count )
return false;
for ( int index = 0; index < item.Count; index++ )
if ( item[index] != list[index] )
return false;
}
return true;
}
}
假设{ 1, 7 }
与{ 7, 1 }
不同,否则您需要更新算法。
您可以调整list为null的情况以返回true或false。
测试
HashSet<List<int>> _hash = new HashSet<List<int>>();
_hash.Add(new List<int> { 1, 7 });
Console.WriteLine(_hash.ContainsSequence(new List<int> { 1, 7 }));
Console.WriteLine(_hash.ContainsSequence(new List<int> { 7, 1 }));
Console.WriteLine(_hash.ContainsSequence(new List<int> { 1, 7, 5 }));
Console.WriteLine(_hash.ContainsSequence(new List<int> { 1 }));
Console.WriteLine(_hash.ContainsSequence(new List<int>()));
Console.WriteLine();
_hash.Clear();
Console.WriteLine(_hash.ContainsSequence(new List<int> { 1, 7 }));
Console.WriteLine(_hash.ContainsSequence(new List<int>()));
Console.WriteLine();
_hash.Add(new List<int>());
Console.WriteLine(_hash.ContainsSequence(new List<int>()));
Console.WriteLine(_hash.ContainsSequence(null));
Console.WriteLine(HashSetHelper.ContainsSequence(null, null));
Console.WriteLine();
_hash.Add(null);
Console.WriteLine(_hash.ContainsSequence(null));
输出
True
False
False
False
False
False
False
True
False
False
True
注意事项
您可以使用List<List<int>>
或HashSet<HastSet<int>>
代替HashSet<List<int>>
。
但是请记住,HashSet不允许重复。
HashSet快速稳定。