C# 如何从列表中删除重复项<;列表<;T>&燃气轮机;对于值类型T?

C# 如何从列表中删除重复项<;列表<;T>&燃气轮机;对于值类型T?,c#,linq,C#,Linq,这是一个如此简单的问题,肯定已经有人问过并回答过了。。。但是我找不到它 我想使用LINQ从值类型列表中删除重复项。我尝试了以下方法: List<List<int>> a = new List<List<int>>() { new List<int>() { 1, 2, 3 }, new List<int>() { 1, 2, 3 }, new List<int>() { 2, 3, 4 } }; // remov

这是一个如此简单的问题,肯定已经有人问过并回答过了。。。但是我找不到它

我想使用LINQ从值类型列表中删除重复项。我尝试了以下方法:

List<List<int>> a = new List<List<int>>() { new List<int>() { 1, 2, 3 }, new List<int>() { 1, 2, 3 }, new List<int>() { 2, 3, 4 } };
// remove duplicates from a
List<List<int>> b = a.Distinct().ToList(); // this doesn't do it
List<List<int>> c = a.Distinct(new ListKeyComparer<int>()).ToList(); // nor does this

internal class ListKeyComparer<TKey> : IEqualityComparer<List<TKey>>
{
  public bool Equals(List<TKey> key1, List<TKey> key2)
  {
    return String.Join("_", key1).Equals(String.Join("_", key2));
  }

  public int GetHashCode(List<TKey> key)
  {
    return key.GetHashCode();
  }
}
List a=newlist(){newlist(){1,2,3},newlist(){1,2,3},newlist(){2,3,4};
//从文件中删除重复项
列表b=a.Distinct().ToList();//这不行
列表c=a.Distinct(新的ListKeyComparer()).ToList();//这也不是
内部类ListKeyComparer:IEqualityComparer
{
公共布尔等于(列表键1,列表键2)
{
返回String.Join(“_”,key1).Equals(String.Join(“_”,key2));
}
public int GetHashCode(列表键)
{
return key.GetHashCode();
}
}

欢迎所有解决方案

您需要的是序列的
IEqualityComparer
。这并不特别困难。(请注意,您可以三次有效地将示例概括为泛型,而不是特定于
int
,并使用
IEnumerable
而不是
List
,因为您不需要特定于列表的功能

public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    private IEqualityComparer<T> comparer;
    public SequenceComparer(IEqualityComparer<T> comparer = null)
    {
        comparer = comparer ?? EqualityComparer<T>.Default;
    }
    public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
    {
        return x.SequenceEqual(y, comparer);
    }

    public int GetHashCode(IEnumerable<T> sequence)
    {
        unchecked
        {
            int hash = 19;
            foreach (var item in sequence)
                hash = hash * 79 + comparer.GetHashCode(item);
            return hash;
        }
    }
}
公共类SequenceComparer:IEqualityComparer
{
私人IEqualityComparer比较器;
公共SequenceComparer(IEqualityComparer comparer=null)
{
比较器=比较器??相等比较器。默认值;
}
公共布尔等于(IEnumerable x,IEnumerable y)
{
返回x.SequenceEqual(y,比较器);
}
public int GetHashCode(IEnumerable序列)
{
未经检查
{
int hash=19;
foreach(序列中的var项目)
hash=hash*79+比较器.GetHashCode(项);
返回散列;
}
}
}
Equals
是以
SequenceEqual
的形式免费提供的。唯一有趣的事情是根据序列中的值创建一个有意义的哈希,而不是使用序列本身提供的
GetHashCode
方法,因为它通常不会这样做(大多数
IEnumerable
,包括
List
,其哈希代码将基于对类的引用,而不是类中的值)

在这种情况下,不需要为该
SequenceComparer
提供项目类型的内部比较器(在这种情况下,
int
)因为默认的相等性应该正是您所需要的。如果您有一个
列表
,并且您想比较列表的相等性并对字符串进行不区分大小写的比较,那么您可以使用一个
新的SequenceComparer(StringComparer.InvariantCultureIgnoreCase)


请注意,对项的字符串值进行编码并不是比较两个序列的特别安全的方法。对象可能没有有意义的
ToString
方法。(任何不重写
ToString
的类型都只会打印出类型名,这意味着所有内容都将与其他内容相等。)您还需要处理冲突的情况。例如,如果您有一个项目生成的字符串值为
“1\u 2”
,则该项目将被视为等于两个不同的项目,每个项目生成
“1”
“2”

实现的问题是,它使用了
列表的
直接
GetHashCode。您可以将其替换为“键字符串”的哈希代码,该哈希代码是通过将数字与下划线连接或动态计算哈希代码构建的:

// Here is a fix to your method. It would work if TKey values
// cannot have underscores. In any event, it will be very slow.
internal class ListKeyComparer<TKey> : IEqualityComparer<List<TKey>>
{
  // Make a method that produces the key to avoid repeating yourself:
  private string MakeKey(List<TKey> key) {
    return String.Join("_", key);
  }
  public bool Equals(List<TKey> key1, List<TKey> key2)
  {
    return MakeKey(key1).Equals(MakeKey(key2));
  }

  public int GetHashCode(List<TKey> key)
  {
    return MakeKey(key).GetHashCode();
  }
}
//这里是对您的方法的修复。如果TKey值
//不能有下划线。无论如何,它都会非常慢。
内部类ListKeyComparer:IEqualityComparer
{
//创建一个生成密钥的方法以避免重复:
私有字符串生成键(列表键){
返回字符串。Join(“\u1”,键);
}
公共布尔等于(列表键1,列表键2)
{
返回MakeKey(key1).Equals(MakeKey(key2));
}
public int GetHashCode(列表键)
{
返回MakeKey(key.GetHashCode();
}
}
下面是一个更好的实现:

internal class ListKeyComparer<TKey> : IEqualityComparer<List<TKey>>
{
  public bool Equals(List<TKey> key1, List<TKey> key2)
  {
    return key1.SequenceEqual(key2);
  }

  public int GetHashCode(List<TKey> key)
  {
    return key.Aggregate((p, v) => 31*p + v.GetHashCode());
  }
}
内部类ListKeyComparer:IEqualityComparer
{
公共布尔等于(列表键1,列表键2)
{
返回键1.SequenceEqual(键2);
}
public int GetHashCode(列表键)
{
return key.Aggregate((p,v)=>31*p+v.GetHashCode());
}
}
这种实施方式之所以更好,有三个原因:

  • 更具可读性-每个方法都是一行,或多或少是不言自明的(假设您熟悉计算多部分密钥的哈希代码)
  • 效率更高-此代码避免了在散列密钥的过程中重复创建字符串
  • 它提高了正确性-即使
    TKey
    字符串包含下划线,此实现也能正常工作

该实现使用LINQ方法并缩短
Equals
GetHashCode

的代码。您能描述一下您想要得到的确切算法吗?例如,当发现重复项时,哪个列表具有优先级,第一个列表?[[1,2,3],[1,4,5]]应该返回[[2,3],[1,4,5]]或[[1,2,3],[4,5]]?或者您希望删除完全相同的项目列表?订单是否算数?…正如您所看到的,我们需要更多信息:)我认为您对从集合中删除重复项的理解是错误的。您考虑的场景是
列表{1,2,2,3,3}
其中
a.Distinct()
会给你一个不同的列表。你实际上拥有的是一个列表列表,是的,每个列表可能包含数字1,但由于列表中的每个列表并不完全相同,它们实际上从一开始就是不同的!也许看一看和-扩展谢谢大家花时间——下面的答案已经做到了。非常好--非常感谢你,请进来