【问题标题】:Comparing two objects of unknown type for sorting in .NET比较两个未知类型的对象以在 .NET 中进行排序
【发布时间】:2014-12-05 16:01:14
【问题描述】:

我的任务:我有一个对象值数组,我需要对它们进行排序。在大多数情况下,值具有相同的原始数据类型(Int32、DateTime 或 String);但是,在其他情况下,我们可以在此数组中包含多种类型的数据(例如整数和多个字符串)。存储在数组中的值的类型在编译时是未知的。

为了解决这个任务,我编写了一个自定义比较器,用于相应的 `Array.Sort(Array array, IComparer comparer))' 调用。比较两个对象值的方法如下所示:

public static int CompareObjects(object valueX, object valueY)
{
    IComparable myValueX = valueX as IComparable;
    IComparable myValueY = valueY as IComparable;
    if (myValueX == null)
    {
        if (myValueY == null)
            return 0;
        return -1;
    }
    if (myValueY == null)
        return 1;

    if (!Type.Equals(myValueX, myValueY))
        return string.CompareOrdinal(myValueX.GetType().Name, myValueY.GetType().Name);

    return myValueX.CompareTo(myValueY);
}

我的测试表明这种方法是一个瓶颈,我需要加快速度。有没有更快的方法来比较两个未知类型的值?

【问题讨论】:

  • Type.Equals 并没有按照你的想法去做。
  • 是否有理由需要根据名称字符串的序数排序不同的类型?在我看来,这就像一个性能狂。对不同类型的哈希码进行排序可能会有更好的结果,然后仅在哈希码相同时才使用字符串。 @SLaks:我认为Type.Equals 完全符合他的要求——因为他希望它们具有某种一致的排序方式。
  • @SLaks,为什么?我只需要区分两种不同的数据类型,如果它们不同就不要比较它们。也许,对于那个“如果”,一个更好的测试表达式是myValueX.GetType() != myValueY.GetType()
  • @James,你是说myValueX.GetHashCode().CompareTo(myValueY.GetHashCode())吗?为什么会更好?
  • 没有。我会做这样的事情: if (!Type.Equals(myValueX, myValueY)) { int ret = myValueX.GetType().GetHashCode().CompareTo(myValueY.GetType().GetHashCode());如果 (ret != 0) 返回 ret; return string.CompareOrdinal(myValueX.GetType().Name, myValueY.GetType().Name);这首先比较类型的哈希码,这比比较类型的名称要快得多,并且仅在哈希码相同的极少数情况下才使用比较名称。

标签: c# arrays sorting compare


【解决方案1】:

如果您不需要主要按类型名称排序的对象(函数成本的很大一部分),这可以修复类型比较错误,并在一个简单的测试用例中使用不同类型都实现 IComparable,还提供了 25% 的实测性能提升(在没有 Type.GetHashCode() 优化的相同功能上):

    public static int CompareObjects(object valueX, object valueY)
    {
        IComparable myValueX = valueX as IComparable;
        IComparable myValueY = valueY as IComparable;
        if (myValueX == null)
        {
            if (myValueY == null)
                return 0;
            return -1;
        }
        if (myValueY == null)
            return 1;
        Type typeX = valueX.GetType();
        Type typeY = valueY.GetType();
        if (!typeX.Equals(typeY))
        {
            int ret = typeX.GetHashCode().CompareTo(typeY.GetHashCode());
            if (ret != 0) return ret;
            return string.CompareOrdinal(typeX.Name, typeY.Name);
        }

        return myValueX.CompareTo(myValueY);
    }

请注意,此实现也存在问题,因为它无法正确比较未从 IComparable 继承的对象。这对您来说可能是也可能不是问题。

对于持怀疑态度的人,这里有一个简单的程序,证明在 Types 上比较 GetHashCode() 是一种性能改进:

class Program
{
    class Comparable1 : IComparable<Comparable1>, IComparable
    {
        private int _value;
        public Comparable1()
        {
            _value = new Random().Next();
        }
        public int CompareTo(Comparable1 other)
        {
            return _value.CompareTo(other._value);
        }

        public int CompareTo(object obj)
        {
            return CompareTo(obj as Comparable1);
        }
    }
    class Comparable2 : IComparable<Comparable2>, IComparable
    {
        private int _value;
        public Comparable2()
        {
            _value = new Random().Next();
        }
        public int CompareTo(Comparable2 other)
        {
            return _value.CompareTo(other._value);
        }

        public int CompareTo(object obj)
        {
            return CompareTo(obj as Comparable2);
        }
    }
    static void Main(string[] args)
    {
        Comparable1 x = new Comparable1();
        Comparable2 y = new Comparable2();
        Stopwatch version1 = Stopwatch.StartNew();
        for (int round = 0; round < 10000000; ++round)
        {
            CompareObjects1(x, y);
        }
        version1.Stop();
        Stopwatch version2 = Stopwatch.StartNew();
        for (int round = 0; round < 10000000; ++round)
        {
            CompareObjects2(x, y);
        }
        version2.Stop();
        Console.WriteLine("Time1=" + version1.ElapsedTicks.ToString() + ", Time2=" + version2.ElapsedTicks.ToString());
    }
    public static int CompareObjects1(object valueX, object valueY)
    {
        IComparable myValueX = valueX as IComparable;
        IComparable myValueY = valueY as IComparable;
        if (myValueX == null)
        {
            if (myValueY == null)
                return 0;
            return -1;
        }
        if (myValueY == null)
            return 1;
        Type typeX = valueX.GetType();
        Type typeY = valueY.GetType();
        if (!typeX.Equals(typeY))
        {
            return string.CompareOrdinal(typeX.Name, typeY.Name);
        }

        return myValueX.CompareTo(myValueY);
    }
    public static int CompareObjects2(object valueX, object valueY)
    {
        IComparable myValueX = valueX as IComparable;
        IComparable myValueY = valueY as IComparable;
        if (myValueX == null)
        {
            if (myValueY == null)
                return 0;
            return -1;
        }
        if (myValueY == null)
            return 1;
        Type typeX = valueX.GetType();
        Type typeY = valueY.GetType();
        if (!typeX.Equals(typeY))
        {
            int ret = typeX.GetHashCode().CompareTo(typeY.GetHashCode());
            if (ret != 0) return ret;
            return string.CompareOrdinal(typeX.Name, typeY.Name);
        }

        return myValueX.CompareTo(myValueY);
    }
}

【讨论】:

  • 这不能解决the OP's bug。此外,它只会减慢代码的速度。当哈希码允许您通过哈希轻松地将一个对象与大量其他对象“比较”时,它们就很有用。当只有一个对象可以与之比较时,这不会是一场净胜。
  • 我现在已经修复了这个错误。由于GetHashCode() 比类型名称的字符串比较快得多,因此性能显着提高(25%)(实测)。一个比较指针,另一个比较可变长度Int16序列。
  • 不,真的不是。它们将非常具有可比性。实际上很有可能它会更慢。散列的使用在基于散列的表的上下文中很有用,其中使用散列来查找相应的散列桶允许在概念上将一个对象与 N 个对象进行比较,而将它们与一个对象进行比较所需的工作量相同。固定数量的项目。当您拥有一个很棒并且可以有巨大性能改进的大型集合,但它需要有大量的值来比较净胜。你只有 2 个。
  • 字符串的哈希码依赖于它的指针。相等的字符串需要具有相等的散列,并且您可以有多个具有逻辑相等值的字符串实例(每个都有自己的引用)。哈希必须基于字符串中的实际字符。
  • @TecMan:如果排序需要在运行中保持一致,那么 GetHashCode 的使用肯定会被淘汰,即使对于字符串也是如此。假设排序发生很多,并且您提前知道将涉及的所有类型,您可以使用排序代码(基于类型名称字符串比较)创建一个全局字典,并使用它对不同类型进行排序。这将允许一致的排序,并且仍然比比较字符串快得多。您甚至可以使用 ConditionalWeakTable 动态构建它。这有一些开销,但如果你要进行大量排序......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 1970-01-01
  • 2010-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多