2013-04-26 38 views
6

我要排序,可以包含不同的数值类型(双,浮点等等)的阵列此代码引发一个System.ArgumentException(“值不是System.Single”)错误:如何排序异构数字的动态数组? 。

new dynamic[] { 5L, 4D, 3F, 2U, 1M, 0UL }.ToList().Sort(); 

我知道我可以使用LINQ来做到这一点:

new dynamic[] { 5L, 4D, 3F, 2U, 1M, 0UL }.ToList().OrderBy(x => (decimal)x).ToArray(); 

但是有没有不涉及通过任何有施放的每个元素会更快的方法?

附录:我也希望能够处理列表中的空值,所以即使LINQ查询的投射也无济于事。

+1

最终,为了比较两个数字,它们必须是兼容的类型。我不确定你可以绕过这个限制。每种类型使用'IComparable.CompareTo(object)'实现会更糟糕,因为每个调用都需要装箱参数。 – cdhowie 2013-04-26 19:36:40

+1

为什么你有一组不同的数字开始? – Stilgar 2013-04-26 19:42:06

+1

Stilgar:我使用JSON.NET的动态加载从JSON文件加载它们,这似乎将数字转换为双精度或整数,具体取决于它们是否有小数点。 – 2013-04-26 20:04:57

回答

5

首先,删除ToList()。此操作不是必需的,只需将其删除即可。这会加快一点。

其余:没有没有更快的方法。如果首先发布的代码显示性能提高的因子为2的代码的答案。但是,当我运行比代码更大的数组时,它变得相对较慢,并且比原始代码更慢。

为什么?整个OrderBy分为两部分:通过比较这些键来生成键和值的排序。如果数组中的项目数量增加,则密钥的生成将逐行增长,但比较操作的数量呈指数增长。

我的代码并不需要将所有值都转换为小数(密钥生成期间的速度增加),但是在排序过程中需要取消这个值,这是性能下降。随着阵列数量的增加,比较操作的数量呈指数级增长,因此拆箱的数量增加,最终得到的是沙子中的沙子。

我试过其他的解决方案,比如创建接受两种数字类型的代表,并且为这两种类型委托一个动态的编译代码片段和最佳比较解决方案,这总是涉及到有时需要转换一个数字。在分拣期间,这变成了杀戮。

所以简单地说:不,你的例程不能做得更快。只要比较结果尽可能快,关键代码的使用时间并不重要。

谁的人有兴趣,从我以前的答案TE原代码(这是不是更快与更大的阵列):

static private dynamic[] testSet = new dynamic[] { 5L, 4D, 3F, null, 2U, 1M, null, 0UL }; 

    static void Main(string[] args) 
    { 
     Stopwatch st1 = new Stopwatch(); 
     st1.Start(); 
     for(int i = 0; i < 100000; i++) 
      Test1(); 
     st1.Stop(); 

     Stopwatch st2 = new Stopwatch(); 
     st2.Start(); 
     for(int i = 0; i < 100000; i++) 
      Test2(); 
     st2.Stop(); 
    } 

    static public void Test1() 
    { 
     var result = testSet.OrderBy(x => x == null ? (decimal?)null : (decimal)x).ToArray(); 
    } 

    static public void Test2() 
    { 
     var result = testSet.OrderBy(x => (object)x, new HeterogeneousNumbersComparer()).ToArray(); 
    } 

    public class HeterogeneousNumbersComparer : IComparer<object> 
    { 
     public int Compare(object a, object b) 
     { 
      if (a == null) 
       if (b == null) 
        return 0; 
       else 
        return -1; 
      else if (b == null) 
       return +1; 

      if (a.GetType() == b.GetType()) 
      { 
       switch(Type.GetTypeCode(a.GetType())) 
       { 
        case TypeCode.Byte: 
         return ((Byte)a).CompareTo((Byte)b); 
        case TypeCode.Decimal: 
         return ((Decimal)a).CompareTo((Decimal)b); 
        case TypeCode.Double: 
         return ((Double)a).CompareTo((Double)b); 
        case TypeCode.Int16: 
         return ((Int16)a).CompareTo((Int16)b); 
        case TypeCode.Int32: 
         return ((Int32)a).CompareTo((Int32)b); 
        case TypeCode.Int64: 
         return ((Int64)a).CompareTo((Int64)b); 
        case TypeCode.SByte: 
         return ((SByte)a).CompareTo((SByte)b); 
        case TypeCode.Single: 
         return ((Single)a).CompareTo((Single)b); 
        case TypeCode.UInt16: 
         return ((UInt16)a).CompareTo((UInt16)b); 
        case TypeCode.UInt32: 
         return ((UInt32)a).CompareTo((UInt32)b); 
        case TypeCode.UInt64: 
         return ((UInt64)a).CompareTo((UInt64)b); 
       } 
      } 
      return Convert.ToDecimal(a).CompareTo(Convert.ToDecimal(b)); 
     } 
    } 
} 

的数字(我的机器上是):
的Test1:550ms
Test2:263ms
因此...一个因素2!