2015-08-26 4 views
1

J'ai une ArrayList où je stocke 10 objets avec deux nombres générés au hasard chacun et je veux trier la liste par les valeurs Y des objets.Trier ArrayList avec des objets bidimensionnels

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); 

La méthode Sort génère l'exception suivante:

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

Je suppose que cela ne fonctionne pas parce que la méthode de tri ne peut pas gérer deux objets tridimensionnels dans les ArrayLists. Comment puis-je faire fonctionner la méthode Sort maintenant?

+3

Pourquoi ne pas utiliser 'List ' au lieu de 'ArrayList'? – juharr

+0

Comment vous attendiez-vous à ce que la méthode Sort sache que vous vouliez trier par Y et non par X? Utilisez 'List ' et 'list = list.OrderBy (t => t.Y) .ToList();'. Vous ne devriez plus utiliser ArrayList, maintenant qu'il existe de belles collections génériques comme 'List '. – Blorgbeard

Répondre

4
ArrayList CustomList = new ArrayList(); 

est juste une liste dimensions - le fait qu'un Point a deux dimensions n'a rien à voir avec la dimensionnalité de la liste. ressemblerait à ceci un tableau à deux dimensions de points (Point[][]):

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

Le problème est que Point n'implémente pas l'interface IComparable par défaut. Cela a du sens - pouvez-vous dire si (1, 3) est supérieur ou inférieur à (2, 2)? Si vous comparez par première coordonnée, elle est plus petite, mais si vous comparez par la distance à l'origine, elle est plus grande.

Maintenant que vous avez décidé quelle fonction de comparaison que vous souhaitez utiliser, vous pouvez créer une nouvelle PointComparer classe:

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); 
    } 
} 

Déclarez votre CustomList comme

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

et trier avec

CustomList.Sort(new PointComparer()); 
+0

Informatif, mais cela ne répond pas réellement à la question .. – Blorgbeard

+0

Il n'y avait pas de vraie réponse possible à la question originale. J'ai inclus une réponse à la nouvelle version de la question. – Glorfindel

+0

Voici le .net Fiddle https://dotnetfiddle.net/nJjMyg –

1

Le meilleur moyen serait d'utiliser List<Point>, encore même avec ArrayList vous pouvez utiliser 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(); 

Si vous voulez avoir ArrayList comme structure retournée:

ArrayList sortedAL = new ArrayList(sorted); 
0

L'affiche originale dit:

Le Trier la méthode lève l'exception suivante:

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

Je suppose que cela ne fonctionne pas parce que la méthode Sort ne peut pas gérer deux ArrayLists dimensionnelles .

Vous êtes confus sur deux points:

  • D'abord, il ne fonctionne pas à cause de ce que le message d'exception vous a dit: la classe Point ne met pas en oeuvre l'interface IComparable.Cela signifie que votre ArrayList ne sait pas comment comparer deux points:

Comment comparer à Point(1,2)Point(2,1: est la première supérieure, inférieure ou égale à la seconde?

  • Deuxièmement, ArrayList est juste un tableau - une liste - des objets. C'est intrinsèquement unidimensionnel. Il s'agit d'un équivalent de longueur réglable de, disons object[]. Troisièmement, vous devriez éviter d'utiliser les classes de collection non génériques, pour plusieurs raisons.

    1. Ils ne sont pas typées et
    2. Les versions génériques sont beaucoup plus faciles à utiliser car ils sont fortement typés: vous n'avez pas downcaster les références contenues dans le type correct.

Ainsi, compte tenu de cette définition de 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 ; 
    } 
} 

Vous pouvez trier votre ArrayList ainsi:

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()) ; 

Ou vous pouvez utiliser les collections génériques et accomplir la même chose beaucoup plus concis:

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