这个问题很简单,在SO中进行简单的搜索会提出一些类似的问题,但我仍然很难找到答案。
我想根据List<Column> columns
的所有属性删除重复对象。 Column
类本身具有List<string>
属性,我认为我可能在GetHashCode()
或Equals
部分遇到问题。
以下是我编写的完整代码,但我没有得到正确的结果。例如,在下面的代码中,我想删除column3,因为它在每个方面都与column1相同。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Arrays
{
public class ColumnList
{
public static void RemoveDuplicateColumnTypes()
{
// define columns
var column1 = new Column
{
StartElevation = 0,
EndElevation = 310,
ListOfSections = new List<string> { "C50", "C40" }
};
var column2 = new Column
{
StartElevation = 0,
EndElevation = 310,
ListOfSections = new List<string> { "C50", "C30" }
};
var column3 = new Column
{
StartElevation = 0,
EndElevation = 310,
ListOfSections = new List<string> { "C50", "C40"}
};
// list of columns
var columns = new List<Column> { column1, column2, column3 };
var result = columns.Distinct(new ColumnListComparer());
}
}
public class ColumnListComparer : IEqualityComparer<Column>
{
public bool Equals(Column x, Column y)
{
if (x == null || y == null) return false;
if (Math.Abs(x.StartElevation - y.StartElevation) < 0.001 &&
Math.Abs(x.EndElevation - y.EndElevation) < 0.001 &&
x.ListOfSections.SequenceEqual(y.ListOfSections))
{
return true;
}
return false;
}
public int GetHashCode(Column obj)
{
return obj.StartElevation.GetHashCode() ^
obj.EndElevation.GetHashCode() ^
obj.ListOfSections.GetHashCode();
}
}
public class Column
{
public double StartElevation { get; set; }
public double EndElevation { get; set; }
public List<string> ListOfSections { get; set; }
}
}
答案 0 :(得分:2)
更改您的GetHashCode
,如下所示。
public int GetHashCode(Column obj)
{
unchecked
{
var hashCode = obj.StartElevation.GetHashCode();
hashCode = (hashCode * 397) ^ obj.EndElevation.GetHashCode();
foreach (var item in obj.ListOfSections)
{
hashCode = (hashCode * 397) ^ item.GetHashCode();
}
return hashCode;
}
}
您的版本不起作用,因为ListOfSections.GetHashCode
是不确定的,可能会返回任何内容,因为它用于检查引用相等性,但我们在这里处理值相等。所以必须基于“值相等”生成哈希码
答案 1 :(得分:2)
这是罪魁祸首:
return obj.StartElevation.GetHashCode() ^
obj.EndElevation.GetHashCode() ^
obj.ListOfSections.GetHashCode(); // <<== This line
列表不会将其哈希码基于其元素的哈希码。具有相同元素的两个不同列表将不具有相同的哈希码。将此更改为聚合列表成员的哈希码的行,以使其工作:
return 31*31*obj.StartElevation.GetHashCode() +
31*obj.EndElevation.GetHashCode() +
obj.ListOfSections.Aggregate((p, v) => 31*p + v.GetHashCode());
注意:虽然这会使您的代码消失cell3
,您的代码将无效。原因不是那么明显 - 您将遇到的问题是您的Equals
方法不具有传递性,因为您通过允许差异在公差0.001
内被视为相等来比较双精度。这样做的结果是
Cell1 == Cell2 && Cell2 == Cell3
不再暗示
Cell1 == Cell3
这是根本错误的。
此外,这意味着您的算法认为相同的两个对象可能具有不同的哈希码。平等比较的合同禁止这样做。
为了解决此问题,请将代表高程切换为double
,并使用int
或long
来存储以比单位小1000倍的单位表示的高程你现在有。换句话说,如果您的代码将高程存储为double 123.456
,则新代码应存储整数123456
。这可以让你比较平等与正确的容忍度。当您获得外部使用的高程时,将数字转换为double
并除以1000以产生旧结果。
答案 2 :(得分:1)
列表的哈希码不匹配。所以整个哈希码不匹配。
试试这个:
public int GetHashCode(Column obj)
{
return 42;
}
如果对象相等,则哈希码必须相等。一般来说,返回固定数字并不理想,但它有效。为什么42?搭便车的人。
如果可行,那么你可以寻找一个更好的哈希函数,它实际上使用了列表中的一些值。
只要你遵循对象的黄金法则是相同的,你的哈希函数必须相等,你做散列函数的复杂程度取决于你。
这是一个有效的哈希函数,它只考虑列表的长度:
public int GetHashCode(Column obj)
{
return obj.ListOfSections.Count;
}
答案 3 :(得分:-1)
HashSet<T>
只会添加唯一的商品。
或
List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();
答案 4 :(得分:-1)
可能你需要HasSet
在Column类中重写GetHashCode和Equals方法
将您的对象添加到HashSet,您将获得唯一的设置。
例如var columns = new HashSet(columnList);
以下是使用字符串
的示例 public void ListToHashSet()
{
var list = new List<string> { "abc", "efg", "abc", "efg" };
var set = new HashSet<string>(list);
foreach (var item in list)
Console.WriteLine("list:{0}", item);
foreach (var item in set)
Console.WriteLine("set:{0}", item);
}
并检查输出,因为.net字符串类已经具有GetHashCode和Equals的覆盖,您不需要实现,但对于自定义类,您将需要覆盖两者,Gethashcode用于存储和从HashTable和GetEqual检索两个对象的平等。