【问题标题】:Sort ArrayList with two dimensional objects使用二维对象对 ArrayList 进行排序
【发布时间】:2015-08-26 20:02:37
【问题描述】:

我有一个 ArrayList,其中存储 10 个对象,每个对象有两个随机生成的数字,我想按对象的 Y 值对列表进行排序。

Random rndm = new Random();
ArrayList CustomList = new ArrayList();

for (int i = 0; i < 10; i++) 
{
    Point LP = new Point(rndm.Next(50), rndm.Next(50));
    CustomList.Add(LP); 
}

PrintList(CustomList);
CustomList.Sort(); 
PrintList(CustomList);

Sort 方法抛出以下异常:

System.InvalidOperationException: The comparer threw an exception. ---> System.ArgumentException: At least one object must implement IComparable.

我认为它不起作用,因为 Sort 方法无法处理 ArrayLists 中的二维对象。我怎样才能让 Sort 方法现在起作用?

【问题讨论】:

  • 为什么不用List&lt;Point&gt; 而不是ArrayList
  • 您如何期望 Sort 方法知道您想按 Y 而不是 X 排序?使用List&lt;point&gt;list = list.OrderBy(t=&gt;t.Y).ToList();。你不应该再使用 ArrayList 了,现在有像 List&lt;T&gt; 这样的不错的通用集合。

标签: c# sorting icomparable


【解决方案1】:
ArrayList CustomList = new ArrayList();

只是一个一维列表 - Point 有两个维度的事实与列表的维度无关。 Points 的二维数组 (Point[][]) 如下所示:

row col1  col2  col3
1   Point Point Point
2   Point Point Point
3   Point Point Point

问题是Point默认没有实现IComparable接口。这是有道理的——你能判断 (1, 3) 是大于还是小于 (2, 2)?如果按第一个坐标比较,则较小,但如果按与原点的距离比较,则较大。

现在您已经决定了要使用哪个比较函数,您可以创建一个新的PointComparer 类:

public class PointComparer : IComparer<Point> {
    // return -1 if x is the smallest, 1 if y is the smallest, 0 if they're equal
    public int Compare(Point x, Point y) {
        return x.Y.CompareTo(y.Y);
    }
}

将您的CustomList 声明为

List<Point> CustomList = new List<Point>();

然后排序

CustomList.Sort(new PointComparer());

【讨论】:

  • 原始问题没有真正的答案。我已经包含了对新版本问题的答案。
  • 返回 x.Y.Compare(y.Y);需要返回 x.Y.CompareTo(y.Y);
【解决方案2】:

最好的方法是使用List&lt;Point&gt;,即使使用ArrayList,您也可以使用LINQ:

Random rndm = new Random();
ArrayList CustomList = new ArrayList();

for (int i = 0; i < 10; i++) 
{
  Point LP = new Point(rndm.Next(50), rndm.Next(50));
  CustomList.Add(LP); 
}

var sorted = CustomList.Cast<Point>()
                        .OrderBy(p => p.Y)
                        .ToList();

如果您想将ArrayList 作为返回结构:

ArrayList sortedAL = new ArrayList(sorted);

【讨论】:

    【解决方案3】:

    原发帖人说:

    排序方法抛出以下异常:

    System.InvalidOperationException: The comparer threw an exception. --->
       System.ArgumentException: At least one object must implement IComparable.
    

    我认为它不起作用,因为 Sort 方法不能处理两个 维数组列表。

    你对两点感到困惑:

    • 首先,它不起作用,因为异常消息告诉你Point 类没有实现接口IComparable。这意味着您的 ArrayList 不知道如何比较两点:

    Point(1,2)Point(2,1 相比如何:第一个大于、小于或等于第二个?

    • 其次,ArrayList 只是一个对象数组——一个列表。它本质上是一维的。它是一个可调节的长度,相当于object[]

    • 第三,出于几个原因,您应该避免使用非泛型集合类。

      1. 它们不是类型安全的,并且
      2. 泛型版本更易于使用,因为它们是强类型的:您不必将包含的引用向下转换为正确的类型。

    所以,鉴于Point 的这个定义:

    public struct Point
    {
      public int X { get ; set ; }
      public int Y { get ; set ; }
    
      public Point( int x , int y )
      {
         this.X = x ;
         this.Y = y ;
      }
    }
    

    您可以对ArrayList 进行排序:

    public class PointComparer : IComparer
    {
      int IComparer.Compare( object x , object y )
      {
        if ( x != null && !(x is Point) ) throw new ArgumentException();
        if ( y != null && !(y is Point) ) throw new ArgumentException();
    
        if      ( x == null && y == null ) return  0 ; // two nulls are equals
        else if ( x == null && y != null ) return -1 ; // we're collating nulls-low: change to +1 to collate nulls-high
        else if ( x != null && y == null ) return +1 ; // we're collating nulls-low: change to -1 to collate nulls-high
        else // both x and y are non-null
        {
          Point left  = (Point) x ;
          Point right = (Point) y ;
    
          if ( left.Y <  right.Y ) return -1 ;
          else if ( left.Y >  right.Y ) return +1 ;
          else // ( left.Y == right.Y )
          {
            if      ( left.X  < right.X )    return -1 ;
            else if ( left.X  > right.X )    return +1 ;
            else /* ( left.X == right.X ) */ return  0 ;
          }
    
        }
      }
    }
    
    ...
    
    ArrayList listOfPoints = CreateListofPoints();
    listOfPoints.Sort( new PointComparer() ) ;
    

    或者您可以使用通用集合并更简洁地完成相同的事情:

    List<Point> listOfPoints = CreateListOfPoints();
    listOfPoints.Sort( (left,right) => left.Y.CompareTo(right.Y) != 0
                                     ? left.Y.CompareTo(right.Y )
                                     : left.X.CompareTo(right.X)
    ) ;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-19
      • 1970-01-01
      • 1970-01-01
      • 2019-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多