2012-11-26 9 views
5

Ich habe eine Arraylist von VOs. Diese Objekte haben viele Eigenschaften und entsprechende Get/Set-Methoden. Ich möchte diese Array-Liste basierend auf einer Eigenschaft sortieren, die ich in Runtime bekommen werde. Lassen Sie mich im Detail erklären. Meine VO ist wie dieseine Liste von Objekten basierend auf Laufzeiteigenschaft sortieren

public class Employee { 
    String name; 
    String id; 

    private String getName() { 
     return name; 
    } 

    private String getId() { 
     return id; 
    } 
} 

Ich werde einen String ‚sortType‘ in Laufzeit bekommen, die entweder sein kann ‚id‘ oder ‚name‘. Ich möchte die Liste basierend auf dem Wert der Zeichenfolge sortieren.

Ich habe versucht, Komparator und Reflexion zusammen zu verwenden, aber kein Glück. Möglicherweise habe ich es nicht richtig verwendet. Ich möchte keine if-Schleife verwenden und neue Vergleichsklassen erstellen. Irgendwelche anderen Gedanken?

Der Versuch Fang sollte innerhalb der neuen Klasse sein. Hier ist der Arbeitscode. Wenn Sie eine separate Klasse für Komparatoren verwenden möchten, finden Sie diese in @ Bohemys Kommentar unten.

 String sortType = "name"; // determined at runtime 
     Collections.sort(results, new Comparator<Employee>() { 
     public int compare(Employee c1, Employee c2) { 
      try{ 
      Method m = c1.getClass().getMethod("get" + StringUtils.capitalize(sortType)); 
      String s1 = (String)m.invoke(c1); 
      String s2 = (String)m.invoke(c2); 
      return s1.compareTo(s2); 
      } 
      catch (Exception e) { 
       return 0; 
      } 
     } 
     }); 
+2

Bitte Verwenden Sie das entsprechende Sprachtag. – NPE

+0

Ups .. Ich habe es jetzt hinzugefügt. – jijo

+0

Wenn Sie Ihren Code schreiben, der die Reflexion verwendet, können wir Ihnen möglicherweise sagen, was schief gelaufen ist ... – akuhn

Antwort

15

eine Comparator für den Auftrag erstellen Sortierung:

public class EmployeeComparator implements Comparator<Employee> { 

    private final String type; 

    public EmployeeComparator (String type) { 
     this.type = type; 
    } 

    public int compare(Employee e1, Employee e2) { 
     if (type.equals("name")) { 
      return e1.getName().compareTo(e2.getName()); 
     } 
     return e1.getId().compareTo(e2.getId()); 
    } 

} 

Dann ist es

String type = "name"; // determined at runtime 
Collections.sort(list, new EmployeeComparator(type)); 

Die reflektierende Version wäre ähnlich zu verwenden, es sei denn Du würdest nach einer Methode auf dem Objekt von "get" + type (groß geschrieben) suchen und diese auf Comparable setzen und compareTo benutzen (Ich werde versuchen, den Code zu zeigen, aber ich benutze mein iPhone und seine a ein Stück weit, b ut geht hier)

public class DynamicComparator implements Comparator<Object> { 
    private final String type; 
    // pass in type capitalised, eg "Name" 
    // ie the getter method name minus the "get" 
    public DynamicComparator (String type) { 
     this.type = type; 
    } 
    public int compare(Object o1, Object o2) { 
     // try-catch omitted 
     Method m = o1.getClass().getMethod("get" + type); 
     String s1 = (String)m.invoke(o1); 
     String s2 = (String)m.invoke(o2); 
     return s1.compareTo(s2); 
    } 
} 

OK ... Hier ist, wie es ohne zu tun, um eine Klasse zu erstellen, eine anonyme-Klasse (mit Ausnahme der Handhabung so kompiliert Code):

List<?> list; 
final String attribute = "Name"; // for example. Also, this is case-sensitive 
Collections.sort(list, new Comparator<Object>() { 
    public int compare(Object o1, Object o2) { 
     try { 
      Method m = o1.getClass().getMethod("get" + attribute); 
      // Assume String type. If different, you must handle each type 
      String s1 = (String) m.invoke(o1); 
      String s2 = (String) m.invoke(o2); 
      return s1.compareTo(s2); 
     // simply re-throw checked exceptions wrapped in an unchecked exception 
     } catch (SecurityException e) { 
      throw new RuntimeException(e); 
     } catch (NoSuchMethodException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } catch (InvocationTargetException e) { 
      throw new RuntimeException(e); 
     } 
    } 
}); 
+1

Das sieht gut und einfach aus. Vielen Dank !! Will versuchen und es euch wissen lassen. – jijo

+0

Das ist auf jeden Fall sauberer, +1 dafür ... –

+0

Awesome man .. !! Genau das habe ich gesucht!Lass mich es versuchen. – jijo

0

Keep it simple!

Wenn die Wahl nur id oder name -Nutzung eine if-Anweisung ist.

Auswahl zwischen zwei Optionen. Genau dafür wurde es erfunden.

Oder, wenn es viele Eigenschaften ist, dann verwenden Sie Reflexion, oder speichern Sie die Daten in einem Map an erster Stelle. Manchmal ist Map besser als eine Klasse. Insbesondere wenn deine VO keine anderen Methoden als Getter und Setter hat.

Vorsicht, die Verwendung von Reflection ist in diesem Fall möglicherweise unsicher, da Ihr Client bei einem Angriff, der der SQL-Injektion ähnelt, einen beliebigen Ausdruck in die CGI-Parameter injiziert.

+0

Das OP gibt an, dass seine Klasse _many_ properties hat. Ich denke, er hat nur zwei von ihnen gezeigt, um es einfach zu halten. – jahroy

+0

Das ist das Problem. Es ist nicht nur ID und Name, mein VO ist ziemlich groß mit ungefähr 10+ Eigenschaften darin. – jijo

+0

Rational hinzugefügt, warum * NOT * mit Reflektion. – akuhn

1

Gehen Sie wie folgt:

  • den Namen des Feldes von dem Client erhalten
  • den Namen des Getter bauen -> "get" + Feldnamen (nach dem ersten Zeichen Kapitalisierung)
  • versuchen, das Verfahren mit Reflexion zu finden, indem Class.getDeclaredMethod()
  • verwenden, wenn gefunden, rufen Sie das zurückgegebene Methode Objekt auf zwei Instanzen Ihrer VO Klasse
  • die Ergebnisse der aufgerufenen Getter-Methoden verwenden für
+0

Sie können genauso gut auf die Felder zugreifen (wenn die Getter wirklich so trivial sind). – akuhn

Verwandte Themen