【问题标题】:Distinct on List<List<double>>List<List<double>> 上的不同
【发布时间】:2018-07-17 18:42:55
【问题描述】:

genesUsingCrossover 是List&lt;List&lt;double&gt;&gt;

我正在使用以下代码行来计算 List&lt;List&lt;double&gt;&gt; 中不同的 List&lt;double&gt;

int count = genesUsingCrossover.Distinct().Count();

我不确定它是否正确。 genesUsingCrossover 中的元素数为 1250,genesUsingCrossover.Distinct().Count() 返回 1250,所以我假设它们都是不同的 List。但是,查看监视窗口时,我注意到第三个和第四个列表是相同的。

所以,我假设这行代码不正确。有没有办法改进它?并计算不同元素的数量?

【问题讨论】:

  • 要真正获得不同的值,您需要使用 obj.Select(x=> propertyName).Distinct();
  • 你是在计算列表,还是在所有列表中计算双打?

标签: c# distinct-values


【解决方案1】:

List&lt;T&gt; 不会覆盖 Object.Equals,因此两个 List&lt;double&gt; 对象只有在引用相等时才会被视为相等。 (HereDistinct&lt;T&gt;() 的实现,如果你想看看它是如何工作的。)听起来你想考虑两个列表是否相等,如果组成它们的元素相等。为此,您可以使用带有IEqualityComparer&lt;T&gt;overload of Distinct&lt;T&gt;(),它将用于确定两个列表是否相等。因此,在您的情况下,您可以提供一个 IEqualityComparer&lt;List&lt;double&gt;&gt; 的实现来表达您对列表平等的想法。

该实现的外观取决于您何时希望将两个列表视为相等。例如,它们必须具有相同顺序的相同元素集,还是顺序不相关? Stack Overflow 上还有其他问题解释了如何实现这两者。在任何一种情况下,请记住Distinct() 将多次调用您的实现,因此您的算法执行良好很重要。为此,可能值得询问List&lt;List&lt;double&gt;&gt; 是否真的是您想要的数据结构,或者其他选择是否更适合。

【讨论】:

    【解决方案2】:

    事实上,您并没有定义两个列表被视为相等的标准。 这意味着 .NET 默认检查两个列表是否在内存中具有相同的引用,因为 Listreference type

    不知不觉,每个列表都有他的记忆。所以你的列表有 1205 个元素,它返回 1205 个不同的元素。

    正如您的描述,我认为您的标准是:包含相同元素的 2 个列表应该相等。

    Distinct 可以接收一个IEqualityComparer,所以想法是:实现IEqualityComparerList&lt;double&gt;

    class NumberDoubles: IEqualityComparer<List<double>>
    {
        public bool Equals(List<double> x, List<double> y)
        {
            //Check whether the compared objects reference the same data.
            if (Object.ReferenceEquals(x, y)) return true;
    
            //Check whether any of the compared objects is null.
            if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
                return false;
    
            if (x.Count!= y.Count)
                return false;
    
            //Check whether the arrays' values are equal.
            for(int i = 0; i < x.Count; i++){
                if(x[i] != y[i])
                    return false;
            }
    
            // If got this far, arrays are equal
            return true;
        }
    
        // If Equals() returns true for a pair of objects 
        // then GetHashCode() must return the same value for these objects.
    
        public int GetHashCode(List<double> doubleArray)
        {
            //Check whether the object is null
            if (Object.ReferenceEquals(doubleArray, null)) return 0;
    
            //Calculate the hash code for the array
            int hashCode = 0;
            bool isFirst = true;
            foreach(int i in doubleArray){
                if(isFirst) {
                    hashCode = i;
                    isFirst = false;
                }
                else
                {
                    hashCode = hashCode ^ i;
                }
            }
            return hashCode;
        }
    }
    

    和你的代码:

    genesUsingCrossover.Distinct(new NumberDoubles());
    

    【讨论】:

    • 检查效率非常低除了两个列表之外,您可能应该只确保长度相等
    • @ThierryV 我测试了您提出的解决方案,速度非常快。谢谢
    【解决方案3】:

    就像@Joe 说的那样,你应该真的在想“我真的需要List&lt;List&lt;double&gt;&gt; 对我来说这听起来像是一个不适合这份工作的结构。区别可以开箱即用HashSet (不完全在你的情况下) ,但一般来说,每当有人看到 HashSet 时,都希望它敲响人们需要独特性的钟声,而 List&lt;List&lt;double&gt;&gt; 不一定表明这一点。

    话虽如此,我建议您使用HashSet&lt;List&lt;double&gt;&gt; 的以下解决方案

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public static class Program {
        public static void Main() {
            var hashSet = new HashSet<List<double>>(new ListComparer());
    
            hashSet.Add(new List<double> { 1.2d, 1.5d });
            hashSet.Add(new List<double> { 1.2d, 1.5d });
    
            Console.Write(hashSet.Count);
        }
    
        public class ListComparer : IEqualityComparer<List<double>> 
    
        {
            public bool Equals(List<double> x, List<double> y)
            {
                // your logic for equality
                return true;
            }
    
            public int GetHashCode(List<double> obj)
            {
               int hash = 0;
               unchecked {
                   foreach(var d in obj) {
                       hash += d.GetHashCode();
                   }
               }
               return hash;
            }  
        }
    }
    

    请记住,Equals 方法会被调用很多次,因此可能需要牢记一些性能注意事项。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多