2017-06-28 4 views
-1

ich eine Klasse Produkt haben, die drei Variablen:Wie eine Methode compareTo() implementieren, wenn dies mit Gleich und hashcode

class Product implements Comparable<Product>{ 
    private Type type;     // Type is an enum 
    Set<Attribute> attributes;  // Attribute is a regular class 
    ProductName name;     // ProductName is another enum 
} 

I Eklipse verwendet automatisch die gleich() und hashCode() Methoden zu generieren:

@Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((attributes == null) ? 0 : attributes.hashCode()); 
     result = prime * result + ((type == null) ? 0 : type.hashCode()); 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) 
      return true; 
     if (obj == null) 
      return false; 
     if (getClass() != obj.getClass()) 
      return false; 
     Product other = (Product) obj; 
     if (attributes == null) { 
      if (other.attributes != null) 
       return false; 
     } else if (!attributes.equals(other.attributes)) 
      return false; 
     if (type != other.type) 
      return false; 
     return true; 
    } 

Jetzt in meiner Anwendung brauche ich eine Reihe von Produkt zu sortieren, also muss ich die Schnittstelle Comparable und compareTo-Methode implementieren:

@Override 
    public int compareTo(Product other){ 
     int diff = type.hashCode() - other.getType().hashCode(); 
    if (diff > 0) { 
     return 1; 
    } else if (diff < 0) { 
     return -1; 
    } 

    diff = attributes.hashCode() - other.getAttributes().hashCode(); 

    if (diff > 0) { 
     return 1; 
    } else if (diff < 0) { 
     return -1; 
    } 

    return 0; 
    } 

Ist diese Implementierung sinnvoll? Wie wäre es, wenn ich das Produkt nur anhand der String-Werte von "type" - und "attributes" -Werten sortieren möchte? Wie kann man das umsetzen?

Edit: Der Grund, warum ich ein Set von sortieren möchte, ist, weil ich Junit Test habe, der auf den String-Werten eines HashSet behauptet. Mein Ziel ist es, die gleiche Reihenfolge der Ausgabe beizubehalten, wie ich den Satz sortiere. Andernfalls, selbst wenn die Werte des Sets gleich sind, wird die Assertion aufgrund der zufälligen Ausgabe eines Satzes fehlschlagen.

Edit2: Durch die Diskussion ist es klar, dass die Einheit der String-Werte eines HashSet in Unit-Tests nicht gut ist. Für meine Situation schreibe ich im Moment eine sort() -Funktion, um die HashSet-String-Werte in natürlicher Reihenfolge zu sortieren, so dass sie konsistent den gleichen String-Wert für meine Unit-Tests ausgeben kann und das für den Moment ausreicht. Danke allen.

+1

Warum würden Sie ** jemals ** einen hashCode im compareTo verwenden? Macht keinen Sinn. Was müsste nach hashCode sortiert werden? Wie ist das eine "natürliche" Ordnung der Klasse? –

+0

Ok. macht keinen Sinn. Wie implementiert man die natürliche Reihenfolge der Klasse? – user697911

+0

Also, um Ihre Frage zu beantworten, macht keine Ihrer Implementierung absolut keinen Sinn. –

Antwort

0

Diese Implementierung scheint nicht konsistent zu sein. Sie haben keine Kontrolle darüber, wie die hash codes aussehen. Wenn Sie obj1 < obj2 nach compareTo im ersten Versuch haben, das nächste Mal, wenn Sie Ihre JVM starten, könnte es umgekehrt obj1 > obj2 sein.

Das einzige, was Sie wirklich wissen ist, dass wenn diff == 0 dann die Objekte als gleich betrachtet werden. Sie können aber auch einfach die Methode equals für diese Prüfung verwenden.

Es liegt jetzt an Ihnen, wie Sie definieren, wenn obj1 < obj2 oder obj1 > obj2. Stellen Sie nur sicher, dass es konsistent ist.

Übrigens wissen Sie, dass die aktuelle Implementierung ProductName name in der equals Prüfung nicht enthält? Weiß nicht, ob das so gemeint ist.

Die Frage ist, was wissen Sie über diese Attribute? Vielleicht implementieren sie Comparable (zum Beispiel, wenn sie Number s sind), dann können Sie nach ihrer compareTo Methode bestellen. Wenn Sie nichts über die Objekte wissen, wird es schwierig, eine konsistente Reihenfolge aufzubauen.

Wenn Sie nur möchten, dass sie konsistent geordnet werden, aber die Reihenfolge selbst keine Rolle spielt, können Sie sie einfach zur Erstellungszeit id s geben und nach ihnen sortieren. An dieser Stelle können Sie zwar die Hashcodes verwenden, wenn es nicht wichtig ist, dass sie zwischen JVM Anrufe wechseln können, aber nur dann.

+0

Ja, die tatsächliche Reihenfolge spielt keine große Rolle. Ich möchte nur eine konsistente HashSet-Ausgabe basierend auf einer Bestellung erzielen. – user697911

+0

'LinkedHashSet' gibt immer in der Reihenfolge aus, in der Sie die Elemente hinzugefügt haben, vielleicht reicht das. 'HashSet' selbst verwaltet keine Reihenfolge,' TreeSet' hält seinen Inhalt sortiert mit 'compareTo'. Ihre Hashcode-Implementierung definiert eine Reihenfolge, die konsistent ist, solange Sie die 'JVM' nicht neu starten. Beim nächsten Start wird die Reihenfolge wahrscheinlich anders sein, nur gleiche Elemente bleiben gleich, aber der Rest wird sich ändern. – Zabuza

+0

LinkedHashSet wird Auswirkungen auf die Leistung haben, und das möchte ich nicht. Was ist der richtige Weg, um die korrekte und gleiche Reihenfolge eines Produktsets sicherzustellen? – user697911

1

Sieht aus wie alle Kommentare in hier Sie müssen Comparator überhaupt nicht verwenden. Denn:

1) Sie verwenden HashSet, die nicht mit Comparator funktioniert. Es ist nicht bestellt.

2) Sie müssen nur sicherstellen, dass zwei HashSet s mit Product s gleich sind. Das bedeutet, sie sind gleich groß und enthalten den gleichen Satz Product s.

Da Sie bereits hinzugefügt hashCode und equals Methoden Product alles, was Sie tun müssen, ist equals Methode auf diesen HashSet s nennen.

HashSet<Product> set1 = ... 
HashSet<Product> set2 = ... 

assertTrue(set1.equals(set2)); 
+0

das ist wahr, aber in JUnit test, gibt meine Methode einen Satz von Produkt zurück, und es ist nicht bequem für mich, ein anderes Set zu erstellen, da es das Produkt Objekt im Junit Test nicht erstellen kann, da Produkt andere Objekte enthält . Die einfachste Möglichkeit, die Gleichheit des zurückgegebenen Hashsets zu bestätigen, besteht darin, den String-Wert zu verwenden, da ich den String-Wert in Eclipse Junit direkt sehen kann. Und deshalb möchte ich die String-Werte von HashSet bestätigen. – user697911

+1

Dann rufen Sie einfach 'equals' auf den zurückgegebenen' Set's auf. Sie sollten sich niemals auf 'toString' verlassen, außer für die menschliche Visualisierung dieses Objekts. Der 'toString' auf' Set' kann zwischen JUnit-Runs auf verschiedenen Maschinen und Plattformen wechseln. – tsolakp

+0

Um gleich was? Ich habe das zurückgegebene Set, was in Ordnung ist, aber vergleichen Sie es dann mit was? Ich habe kein anderes Set, das mit Equal verglichen werden kann, und deshalb verwende ich den String-Wert des Sets zum Vergleichen, weil der String-Wert bekannt ist und leicht zu bekommen ist, aber ein anderes Set in meinem Junit-Testcode zu erstellen ist hart. – user697911

Verwandte Themen