在C#中从List<T>中移除重复项

629

有没有快速去重C#中通用List的方法?


5
你是否关心结果中元素的顺序?这可能会排除一些解决方案。 - Colonel Panic
3
一行代码解决方案:ICollection<MyClass> withoutDuplicates = new HashSet<MyClass>(inputList); 该代码使用哈希集合(HashSet)来去除输入列表(inputList)中的重复项,并将结果保存在一个不包含重复项的MyClass对象集合(withoutDuplicates)中。 - Harald Coppoolse
这个方法会在哪里被使用? - kimiahdri
32个回答

2

在列表中解决重复问题有很多方法,以下是其中之一:

List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new  List<Container>();
foreach (var container in containerList)
{ 
  Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
  { return (checkContainer.UniqueId == container.UniqueId); });
   //Assume 'UniqueId' is the property of the Container class on which u r making a search

    if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
      {
        filteredList.Add(container);
       }
  }

祝你好运,Ravi Ganesan


2

.Net 2.0 中的另一种方法

    static void Main(string[] args)
    {
        List<string> alpha = new List<string>();

        for(char a = 'a'; a <= 'd'; a++)
        {
            alpha.Add(a.ToString());
            alpha.Add(a.ToString());
        }

        Console.WriteLine("Data :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t); });

        alpha.ForEach(delegate (string v)
                          {
                              if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
                                  alpha.Remove(v);
                          });

        Console.WriteLine("Unique Result :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
        Console.ReadKey();
    }

2
这里有一个简单的解决方案,不需要阅读困难的LINQ或对列表进行任何预先排序。
   private static void CheckForDuplicateItems(List<string> items)
    {
        if (items == null ||
            items.Count == 0)
            return;

        for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
        {
            for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
            {
                if (innerIndex == outerIndex) continue;
                if (items[outerIndex].Equals(items[innerIndex]))
                {
                    // Duplicate Found
                }
            }
        }
    }

使用这种方法,您可以更好地控制重复项。如果您有要更新的数据库,则可以更好地控制它们。 对于innerIndex,为什么不从outerIndex + 1开始,而是每次都从开头开始? - Nolmë Informatique

2
使用 HashSet 可以轻松地完成这个任务。
List<int> listWithDuplicates = new List<int> { 1, 2, 1, 2, 3, 4, 5 };
HashSet<int> hashWithoutDuplicates = new HashSet<int> ( listWithDuplicates );
List<int> listWithoutDuplicates = hashWithoutDuplicates.ToList();

2

David J.的答案是一个很好的方法,不需要额外的对象、排序等。但是它可以被改进:

for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)

因此,外部循环从列表顶部到底部进行,但内部循环在“达到外部循环位置之前”从底部开始。

外部循环确保整个列表被处理,内部循环找到实际的重复项,这些项只会出现在外部循环尚未处理的部分中。

或者,如果你不想为内部循环使用自下而上的方式,你可以让内部循环从outerIndex + 1开始。


2
使用 HashSet: list = new HashSet<T>(list).ToList(); 意思是将列表转换为 HashSet,然后再转换回列表。这样做的好处是去除了重复项,并且可以提高查找速度。

2
所有答案都是复制列表,或创建新列表,或使用缓慢的函数,或只是非常缓慢。
据我了解,这是我知道的最快和最便宜的方法(同时也得到了一位专门从事实时物理优化的经验丰富的程序员的支持)。
最初的回答:所有答案都复制列表、创建新列表或使用缓慢函数,速度很慢。据我所知,这是我知道的最快且最便宜的方法,同时也得到了一位经验丰富的专门从事实时物理优化的程序员的支持。
// Duplicates will be noticed after a sort O(nLogn)
list.Sort();

// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;

int size = list.Count;

// Store the index pointing to the last item we want to keep in the list
int last = size - 1;

// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
    currItem = list[i];

    // If this item was the same as the previous one, we don't want it
    if (currItem == lastItem)
    {
        // Overwrite last in current place. It is a swap but we don't need the last
       list[i] = list[last];

        // Reduce the last index, we don't want that one anymore
        last--;
    }

    // A new item, we store it and continue
    else
        lastItem = currItem;
}

// We now have an unsorted list with the duplicates at the end.

// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);

// Sort again O(n logn)
list.Sort();

最终成本为:

nlogn + n + nlogn = n + 2nlogn = O(nlogn),非常不错。

关于RemoveRange的说明: 由于我们不能设置列表的计数并避免使用Remove函数,因此我不确定此操作的速度,但我猜测这是最快的方式。


1

如果你需要比较复杂的对象,你需要在Distinct()方法中传递一个Comparer对象。

private void GetDistinctItemList(List<MyListItem> _listWithDuplicates)
{
    //It might be a good idea to create MyListItemComparer 
    //elsewhere and cache it for performance.
    List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.Distinct(new MyListItemComparer()).ToList();
        
    //Choose the line below instead, if you have a situation where there is a chance to change the list while Distinct() is running.
    //ToArray() is used to solve "Collection was modified; enumeration operation may not execute" error.
    //List<MyListItem> _listWithoutDuplicates = _listWithDuplicates.ToArray().Distinct(new MyListItemComparer()).ToList();

    return _listWithoutDuplicates;
}

假设您有其他两个类,例如:
public class MyListItemComparer : IEqualityComparer<MyListItem>
{
    public bool Equals(MyListItem x, MyListItem y)
    {
        return x != null 
               && y != null 
               && x.A == y.A 
               && x.B.Equals(y.B); 
               && x.C.ToString().Equals(y.C.ToString());
    }

    public int GetHashCode(MyListItem codeh)
    {
        return codeh.GetHashCode();
    }
}

并且:

public class MyListItem
{
    public int A { get; }
    public string B { get; }
    public MyEnum C { get; }

    public MyListItem(int a, string b, MyEnum c)
    {
        A = a;
        B = b;
        C = c;
    }
}

1
  public static void RemoveDuplicates<T>(IList<T> list )
  {
     if (list == null)
     {
        return;
     }
     int i = 1;
     while(i<list.Count)
     {
        int j = 0;
        bool remove = false;
        while (j < i && !remove)
        {
           if (list[i].Equals(list[j]))
           {
              remove = true;
           }
           j++;
        }
        if (remove)
        {
           list.RemoveAt(i);
        }
        else
        {
           i++;
        }
     }  
  }

0

我有我的方法。我用两个循环来比较列表项,然后删除第二个。

            for(int i1 = 0; i1 < lastValues.Count; i1++)
            {
                for(int i2 = 0; i2 < lastValues.Count; i2++)
                {
                    if(lastValues[i1].UserId == lastValues[i2].UserId)
                    {
                        lastValues.RemoveAt(i2);
                    }
                }
            }

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