从对象列表中删除重复项

4
我有一个名为MyObject的对象,它包含字段:id、a、b、c、e、f。我还有一个列表,包含500,000个项目,现在如何删除所有具有相同参数a、c、f值的重复项?
我正在寻找最快和最有效的方法。
更新: 我实现了比较器。
我的类中的字段类型不同,因此我使用ToString()方法。这是一个好方法吗? IdLoc、IdMet、Ser是长整型? Value是一个对象? IdDataType是长整型?
class Comparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {                
        return x.IdLoc == y.IdLoc && x.IdMet == y.IdMet && x.Ser == y.Ser &&
               x.IdDataType == y.IdDataType && x.Time == y.Time && x.Value == y.Value;
    }

    public int GetHashCode(MyObject obj)
    {
        string idLoc = obj.IdLoc.HasValue ? obj.IdLoc.ToString() : String.Empty;
        string idMet = obj.IdMet.HasValue ? obj.IdMet.ToString() : String.Empty;
        string ser = obj.Ser.HasValue ? obj.Ser.ToString() : String.Empty;
        string value = obj.Value != null ?  obj.Value.ToString() : String.Empty;

        return (idLoc + idMet + ser + value + obj.IdDataType.ToString() + obj.Time.ToString()).GetHashCode();
    }
}

去除重复项
元素数量为566 890
1) 时间:2秒

DateTime start = DateTime.Now;
List<MyObject> removed = retValTmp.Distinct(new Comparer()).ToList();
double sec = Math.Round((DateTime.Now - start).TotalSeconds, 3);

2) 时间: 1.5 秒

start = DateTime.Now;
List<MyObject> retList = new List<MyObject>();
HashSet<MyObject> removed2 = new HashSet<MyObject>(new Comparer());
foreach (var item in retValTmp)
{
    if (!removed2.Contains(item))
    {
        removed2.Add(item);
        retList.Add(item);
    }
}                
double sec2 = Math.Round((DateTime.Now - start).TotalSeconds, 3);

4) 我也尝试了这种方法:

start = DateTime.Now;

var removed3 = retValTmp.Select(myObj => new { myObj.IdLoc, myObj.IdMet, myObj.Ser, myObj.Value, myObj.IdDataType, myObj.Time }).Distinct().ToList();

double sec3 = Math.Round((DateTime.Now - start).TotalSeconds, 3);

时间:0.35秒
但返回的列表不属于我的课程,为什么1和2的列表中元素数量与3的列表不同?
更新2
public int GetDataHashCode(MyObject obj)
{
    long idLoc = obj.IdLoc.HasValue ? obj.IdLoc.Value : 0;
    long idMet = obj.IdMet.HasValue ? obj.IdMet.Value : 0;
    long ser = obj.SerHasValue ? obj.Ser.Value : 0;
    int valueHash = 0;
    if (obj.Value != null)
        valueHash = obj.Value.GetHashCode();
    else
        valueHash = valueHash.GetHashCode();

    return (idLoc.GetHashCode() + idMet.GetHashCode() + ser.GetHashCode() + valueHash  + obj.IdDataType.GetHashCode() + obj.Time.GetHashCode()).GetHashCode();
}

使用:

foreach (MyObject daItem in retValTmp)
{
    int key = GetDataHashCode(daItem);
    if (!clearDict.ContainsKey(key))
        clearDict.Add(key, daItem);
} 

元素数量: 750,000
时间: 0.23秒!


我建议您尝试编写一些代码,然后使用类似Benchmark.NET的基准测试库来查看其性能。一旦您有了解决方案的基准线,就可以尝试其他方法以查看是否可以提高性能。总体而言,最佳情况下的性能将受到您比较的参数类型和集合大小的限制。对于未排序的随机访问集合,最坏情况下的性能始终为O(n ^ 2)。祝好运。 - Brian Driscoll
所有的项目?还是除了其中一个之外的所有项目? - Charles Bretana
使用linq的distinct()方法? - Mat
1
为了给您一个好的答案,如果您还没有看过[ask],那么看一眼可能会有所帮助。如果您能提供一个[mcve],那也可能会很有用。 - Mat
我想删除所有重复的对象,只留下一个给定的对象。 - Drakoo
如果你想进行一些时间基准测试,你应该使用 Stopwatch 类 - Luiso
5个回答

3

如果您追求速度,且不介意使用一些内存,我会建议您使用 HashSet。如果您有兴趣进行自定义比较,则可以创建一个 IEqualityComparer<T>,像这样:

var original = new ArrayList(); // whatever your original collection is 
var unique = new HasSet<YourClass>(new MyCustomEqualityComparer());

foreach(var item in original)
{
    if(!unique.Contains(item))
        unique.Add(item);
}

return unique;

这里的问题是你可能会消耗掉两倍于原始内存。

更新:

我做了一些额外的研究,我认为你可以通过简单地执行以下操作来实现你想要的结果:

var original // your original data
var unique = new HashSet<YourClass>(origin, new CustomEqualityComparer());

这将确保删除重复数据,因为在HashSet中不允许重复。我建议您也查看有关GetHasCode实现指南的这个问题
如果您想了解更多关于HashSet类的信息,请参阅以下链接: 关于HashSet
关于IEqualityComparer构造函数
IEqualityComparer文档 希望这有所帮助。

1

你可以使用Distinct()方法来获取仅具有指定属性不同值的值。
你可以这样做:

List<MyObj> list = new List<MyObj>();

//Run the code that is going to populate your list.
var result = list.Select(myObj => new { myObj.a, myObj.c, myObj.f})
                 .Distinct().ToList();

//Result contains the data based on the difference.

不幸的是,结果不是我的类的列表 :( - Drakoo
仅返回翻译文本:不涉及字节数组。 - Drakoo

1

这个链接中的代码对我非常有帮助。 https://nishantrana.me/2014/08/14/remove-duplicate-objects-in-list-in-c/

public class MyClass
{
public string ID { get; set; }
public string Value { get; set; }

}

List<MyClass> myList = new List<MyClass>();
var xrmOptionSet = new MyClass();
xrmOptionSet.ID = "1";
xrmOptionSet.Value = "100";
var xrmOptionSet1 = new MyClass();
xrmOptionSet1.ID = "2";
xrmOptionSet1.Value = "200";
var xrmOptionSet2 = new MyClass();
xrmOptionSet2.ID = "1";
xrmOptionSet2.Value = "100";
myList.Add(xrmOptionSet);
myList.Add(xrmOptionSet1);
myList.Add(xrmOptionSet2);

// here we are first grouping the result by label and then picking the first item from each group
var myDistinctList = myList.GroupBy(i => i.ID)
.Select(g => g.First()).ToList();

1

你可以始终使用LINQ Distinct() 来实现这一点:

var matches = list.Distinct(new Comparer()).ToList();

但是要让Ditsinct()起作用,您需要为您的类实现比较器:

class Comparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {
        return x.a == y.a && x.c == y.c && x.f == y.f;
    }

    public int GetHashCode(MyObject obj)
    {
        return (obj.a + obj.c + obj.f).GetHashCode();
    }
}

如果字段类型不同且可以为空,该怎么办? - Drakoo

0

一种高效的方法是首先使用快速排序(或类似的n Log n排序),基于(a,c,f)的哈希值进行排序,然后您可以遍历结果列表,在每次(a,c,f)的值更改时选择一个。

这将提供n log n速度的解决方案,这可能是您能做到的最好的。


如果您将元素放入哈希集中,并让集合为您完成工作,会怎样呢?(https://msdn.microsoft.com/zh-cn/library/bb359438.aspx) - danny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接