Testverfahren mit hashCode

Annalena123

Aktives Mitglied
Ich muss die HashSet Methode implementieren, aber ich weiß nicht wie man Schnelligkeit mit HashSet rechnen kann ? kann jemand bitte erklären was genau unter Testfall 1& 2 gemeint ist ?

Schreibe eine neue, effizientere Implementation der Methode hashCode.
Der Hashcode muss dafür weiterhin möglichst leicht zu berechnen sein, aber auch dafür sorgen, dass die Bücher in einer HashMap gleichmäßig auf die verfügbaren Buckets verteilt werden.

⚠️ Achtung: halte dich an den Vertrag zwischen hashCode und equals! Alle Bücher, die mit equals als gleich bewertet werden, müssen auch den gleichen Hashcode haben!



Testverfahren | Methode hashCode
  • Testfall 1 & 2 überprüfen, wie schnell ein HashSet mit tausenden von Büchern gefüllt werden kann. Deine Implementation muss dabei signifikant schneller sein1als der statische Hashcode 0.
    • Schlägt einer dieser Tests fehl, verteilt dein Hashcode die Bücher wahrscheinlich nicht auf ausreichend viele Buckets! Stelle sicher, dass nicht allzu viele nicht gleiche Bücher den gleichen Hashcode haben.
  • Testfall 3 überprüft, dass dein HashSet mit der korrekten Anzahl an Büchern gefüllt wird und keine Duplikate zulässt, also Bücher, die nach equalsgleich sein müssten.
    • Schlägt dieser Test fehl, hast du den Vertrag zwischen hashCode und equals verletzt! Stelle sicher, dass es keine Bücher gibt, die nach equals gleich sind, aber unterschiedliche Hashcodes haben.
    • 1 Der Test ist bestanden, wenn die Implementation mehr als 80% schneller ist (ca. 20 ms). Mit relativ wenig Aufwand kann die Ausführungszeit aber sogar auf unter 3 ms reduziert werden.

    • Java:
      public class Buch
      {
          private final long _isbn;
          private final String _autor;
          private final String _titel;
          private final int _preis;
          private final int _kaufjahr;
      
          /**
           * Erzeugt ein Buch, welches der Sammlung hinzugefuegt
           * werden soll.
           *
           * @param isbn ISBN des Buches
           * @param autor Autor:in des Buches
           * @param titel Titel des Buches
           * @param preis Preis in Eurocent, zu dem das Buch
           *              gekauft wurde
           * @param kaufjahr Jahr, in dem das Buch gekauft wurde
           */
          public Buch (long isbn, String autor, String titel,
                       int preis, int kaufjahr)
          {
              _isbn = isbn;
              _autor = autor;
              _titel = titel;
              _preis = preis;
              _kaufjahr = kaufjahr;
          }
      
          /**
           * Gibt die ISBN des Buches zurueck.
           * @return ISBN
           */
          public long gibISBN()
          {
              return _isbn;
          }
      
          /**
           * Gibt die Autor:in des Buches zurueck.
           * @return Autor:in des Buches
           */
          public String gibAutor()
          {
              return _autor;
          }
      
          /**
           * Gibt den Titel des Buches zurueck.
           * @return Titel des Buches
           */
          public String gibTitel()
          {
              return _titel;
          }
      
          /**
           * Gibt den Preis zurueck, zu dem das Buch gekauft wurde.
           * @return Kaufpreis in Eurocent
           */
          public int gibPreis()
          {
              return _preis;
          }
      
          /**
           * Gibt das Jahr zurueck, in dem das Buch gekauft wurde.
           * @return Kaufjahr
           */
          public int gibKaufjahr()
          {
              return _kaufjahr;
          }
          @Override
      public boolean equals(Object o)
      {
          // ueberpruefe, ob das uebergebene Object ein Buch ist
          if (o instanceof Buch)
          {
              // sichere dem Kompiler zu, dass der Typ wirklich Buch ist
              Buch b = (Buch) o;
              // vergleiche an der typsicheren Variable alle noetigen Eigenschaften
              if(this._titel.equals(b._titel) && this._autor.equals(b._autor)){
                  return true;
                  
          }
          // wenn das Object kein Buch ist, kann es nicht gleich mit einem Buch sein
          return false;
      }
      
         @Override
      public int hashCode() {
          return 0;
      }
        
      }
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Was hast Du Dir denn bezüglich der Methode hashCode überlegt? Hast Du Dich schon einmal damit beschäftigt?

Ansonsten: Wenn Du nur einfach Code haben willst, dann lass Dir die Methode von einer Entwicklungsumgebung generieren....
 

Oneixee5

Top Contributor
aber ich weiß nicht wie man Schnelligkeit mit HashSet rechnen kann ?
Du kannst die Ausführungszeit messen indem du dir die Startzeit als Timestamp in einer Variablen merkst und am Ende des Tests vom aktuellen Timestamp abziehst, s. System.currentTimeMillis()
Die Ausführungszeit wird aber immer von der Hardware und der Auslastung des Systems abhängen, daher sind vermutlich mehrere Messungen notwendig und ein Mittelwert zu verwenden.
Ich persönlich würde vor dem eigentlichen Test eine Art warmup fahren, im einfachsten Fall eine anderes HashSet ein paar 10-tausend mal füllen. So hat die JVM Zeit sich einzupegeln und schon mal ein paar GC's zu fahren.
Das ist natürlich ein sehr einfacher Test, welcher nicht immer exakt gleiche Ergebnisse liefern wird.
 

Annalena123

Aktives Mitglied
Du kannst die Ausführungszeit messen indem du dir die Startzeit als Timestamp in einer Variablen merkst und am Ende des Tests vom aktuellen Timestamp abziehst, s. System.currentTimeMillis()
Die Ausführungszeit wird aber immer von der Hardware und der Auslastung des Systems abhängen, daher sind vermutlich mehrere Messungen notwendig und ein Mittelwert zu verwenden.
Ich persönlich würde vor dem eigentlichen Test eine Art warmup fahren, im einfachsten Fall eine anderes HashSet ein paar 10-tausend mal füllen. So hat die JVM Zeit sich einzupegeln und schon mal ein paar GC's zu fahren.
Das ist natürlich ein sehr einfacher Test, welcher nicht immer exakt gleiche Ergebnisse liefern wird.
Java:
public int hashCode(Object o) {
int time0 =System.currentTimeMillis()
Set<Buch> set = new HashSet<Buch>(1000) ;
int i =0;
while(i<=1000){
set.add(o)
i++
}
int time1 =System.currentTimeMillis()
return time1-time0;
}
Danke !meinst du so etwas am Ende return time1-time0; ? Ich lerne das gerade , weiß ich nicht genau sollte ich in diesem Fall Object o (Exemplar der Klasse Buch) als aktuelle Parameter eingeben und mit set.add(o) HashSet befülle und dann rechnen ?
 
Zuletzt bearbeitet:

Annalena123

Aktives Mitglied
Was hast Du Dir denn bezüglich der Methode hashCode überlegt? Hast Du Dich schon einmal damit beschäftigt?

Ansonsten: Wenn Du nur einfach Code haben willst, dann lass Dir die Methode von einer Entwicklungsumgebung generieren....
was meinst du damit mit dann lass Dir die Methode von einer Entwicklungsumgebung generieren.... ! Es ist nicht ausreichend 2 Objekten nur mit equals zu überprüfen , die müssen auch den gleichen hashCode generieren , damit man sicher stellt , dass die gleich sind z.B. o1.hashCode() == o2.hashCode() !
 

Oneixee5

Top Contributor

Annalena123

Aktives Mitglied
Jaein! Der Ansatz ist nicht so schlecht. Du solltest eine andere Signatur für die Testmethode verwenden, so ist es verwirrend.

Du verwendest gerade mal 1000 mal das selbe Object, also erfüllst die die Anforderungen ('tausenden von Büchern') nicht.
ist das jetzt besser ?
Java:
@Override
public int hashCode(Object o) {
    long StartTime=System.currentTimeMillis();
Set<Buch> set = new HashSet<Buch>(10000) ;
int i =0;
while(i<=10000){
set.add(o);
i++;
}
long EndTime =System.currentTimeMillis();
long CalculateTime= EndTime-StartTime;
return (int)CalculateTime ;
}
ich bekomme jetzt aber zwei Errors :

Syntaxfehler​

tester.java:53: error: method does not override or implement a method from a supertype
@Override
^
tester.java:56: error: cannot find symbol
Set<Buch> set = new HashSet<Buch>(10000) ;
^
symbol: class Set
location: class Buch
2 errors
 

Oneixee5

Top Contributor
@Override public int hashCode(Object o) {
Du sollst einen Test schreiben und nicht die Methode hashCode von "tester.java" überschreiben. Erst später sollst du die Methode hashCode in Buch.java ändern. Halte dich an die Aufgabe und mache nicht einfach irgendwas.

Analysiere die Fehlermeldungen selbst, dann verstehst du auch was du ändern musst:
tester.java:56: -> Der Fehler ist in Zeile 56
^ -> Zeigt genau auf den Fehler
Zeile: 56 -> cannot find symbol -> also ^ zeigt ganau auf "Set" -> Meldung: cannot find symbol -> Woher kommt denn "symbol"? -> Aha es müsste einen Import geben -> schaut man als erstes ob der Import vorhanden ist...
Also siehst du selbst - Fehlermeldungen lesen - Fehler beheben - geht schneller als im Forum danach fragen und auf eine Antwort zu warten.
 

Annalena123

Aktives Mitglied
Du sollst einen Test schreiben und nicht die Methode hashCode von "tester.java" überschreiben. Erst später sollst du die Methode hashCode in Buch.java ändern. Halte dich an die Aufgabe und mache nicht einfach irgendwas.
Danke für den Hinweis !ich bin nicht erlaubt etwas zu importieren ! Das ist eine automatische Test-Umgebung ! Mir wurde nur diese Test Umgebung mit
Java:
@override
public int hashCode(){
   
}
gegeben wenn ich so schreibe außer@ override bekomme ich keinen Error !
Java:
@Override
public int hashCode(Object o) {
long StartTime=System.currentTimeMillis();
HashSet<Buch> set = new HashSet<Buch>(10000) ;
int i =0;
while(i<=10000){
set.add( (Buch) o);
i++;
}
long EndTime =System.currentTimeMillis();
long CalculateTime= StartTime-EndTime;
return (int)CalculateTime ;
}
ich habe nicht genau verstanden wie soll ich Test schreiben ? Ich habe nur diese eine Seite! kannst du bitte mehr darüber erklären ?
 

temi

Top Contributor
Nur eine Anmerkung: Die Methode hashCode() der Klasse Buch soll nur einen Hashcode für "dieses" eine bestimmte Buch erzeugen und nicht für mehrere Bücher. Kann sie auch gar nicht, weil sie nur "sich selbst" (= this) kennt. Siehe auch die Doku von hashCode(). Die Methode hat KEINEN Parameter (da hast du den Vorteil von @Override erfahren, du überschreibst nicht, sondern überlädtst)!

EDIT: Für den Test (das macht vermutlich der Test für dich) werden dann viele Bücher erzeugt und im HashSet wird jeweils die hashCode()-Methode jedes dieser Bücher aufgerufen. Da musst du dich nicht drum kümmern. Das macht HashSet selbst.

Damit sie die Vorgabe bezüglich equals() erfüllt muss sie die selben internen Daten berücksichtigen. Wenn für Gleichheit Titel und Autor relevant sind, dann sind sie es auch für hashCode() (und für gleiche Werte muss natürlich das Ergebnis immer gleich sein).
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
was meinst du damit mit dann lass Dir die Methode von einer Entwicklungsumgebung generieren.... ! Es ist nicht ausreichend 2 Objekten nur mit equals zu überprüfen , die müssen auch den gleichen hashCode generieren , damit man sicher stellt , dass die gleich sind z.B. o1.hashCode() == o2.hashCode() !
Die großen Entwcklugsumgebungen (IntelliJ, Eclipse nd Netbeans) können auf Knopfdruck zu einer Entity de hashCode und equals Methode erzeugen. Damit hast Du direkt die Lösung zu der Aufgabe.

ABER: Damit hast Du absolut nichts verstanden. Man sollte schon genau wissen, was man macht und wie es zusammen hängt. Und dazu gibt es auch zig Webseiten, die etwas dazu schreiben.

Aber die Klasse umfasst diese Felder:
Java:
public class Buch
{
    private final long _isbn;
    private final String _autor;
    private final String _titel;
    private final int _preis;
    private final int _kaufjahr;
}

Dann erst einmal die Frage: Was heisst gleich? Wann sind zwei Bücher gleich? Das muss man klar definieren. Wenn die isbn gegeben ist, dann würde es z.B. ausreichen, wenn die isbn gleich ist. Denn die ISBN sollte eindeutig sein. (Es kann nicht zwei Bücher mit der gleichen ISB geben!)

Das bedeutet: Es kann Sinn machen, fachliches Wissen einfließen zu lassen.

Aber ohne dieses Wissen nehmen wir einfach einmal an, dass etwas gleich ist, wenn alle Felder gleich sind. ==> equals Methode.

Nun kann man einen Hashcode berechnen aus allen Feldern. Dazu muss man sich überlegen: Wie geht man da vor?
Generell kann man alle ermittelten Hash Codes einfach per xor verknüpfen. Bei int ist der hashcode aber der Wert vom int und da kann es dann blöd sein mit der Verteilung. Daher kann man dann z.B. noch eine Verschiebung um paar Stellen machen. Also etwas wie z.B.

_kaufjahr << 16
Um 16 Bit wird es verschoben.

Damit solltest Du nun in der Lage sein, die Methode selbst zu schreiben, oder?
 

KonradN

Super-Moderator
Mitarbeiter
Ach ja - man kann das mit dem Hashcode ermitteln auch ganz einfach mit Objects.hash machen - wenn man also das Framework etwas kennt, dann wird es schnell sehr einfach.
 

temi

Top Contributor
Noch eine Ergänzung. Der Effizienzgewinn wird meiner Ansicht nach nicht entstehen, weil die Methode hashCode() super schnell einen Hashcode generieren kann. Schneller als die Rückgabe von "0" geht kaum.

Es geht um die Effizienz beim Einfügen eines Buchs in ein HashSet: Thema "Buckets". Steht ja auch so da. Dazu sollte hashCode() für jedes einzelne Buch (außer Bücher sind gleich) einen unterschiedlichen Hashwert erzeugen.

EDIT: Siehe auch (wie immer) die Doku von HashSet:
[..] This class offers constant time performance for the basic operations (add, remove, contains and size), assuming the hash function disperses the elements properly among the buckets. [..]
Die Zeitmessung dazu musst du vermutlich auch nicht selbst schreiben. Die ist im Test enthalten, würde ich meinen.
 
Zuletzt bearbeitet:

temi

Top Contributor
Es wird wohl um den Effizenzgewinn beim Zugriff auf eine Collection gehen, nicht um die Effizienz der HashCode generierung...
Ja, das hab ich doch so geschrieben. Die Formulierung der Aufgabe ist allerdings auch zunächst irreführend.
Schreibe eine neue, effizientere Implementation der Methode hashCode.
Das wird erst etwas später halbwegs aufgeklärt.
wie schnell ein HashSet mit tausenden von Büchern gefüllt werden kann
 

Oneixee5

Top Contributor
Hier scheitert der TE aber bereits beim Erstellen eines einfachen Tests, bei welchem eine Anzahl Bücher in ein HashSet eingefügt werden soll. Der erste Satz von Aufgabe 1 ist das Problem zum Scheitern. Da geht es noch gar nicht um Hashcodes. Es geht um Textverständnis und ein paar einfache Grundlagen.
 

temi

Top Contributor
Hier scheitert der TE aber bereits beim Erstellen eines einfachen Tests, bei welchem eine Anzahl Bücher in ein HashSet eingefügt werden soll. Der erste Satz von Aufgabe 1 ist das Problem zum Scheitern. Da geht es noch gar nicht um Hashcodes. Es geht um Textverständnis und ein paar einfache Grundlagen.
Der TE muss den Test ja gar nicht schreiben. Der ist bereits vorgegeben! Es soll nur eine sinnvolle hashCode() Methode geschrieben werden, welche mehr tut, als "0" zu liefern, um die Tests zu bestehen. Das ist zumindest mein Textverständnis.
 

Annalena123

Aktives Mitglied
Noch eine Ergänzung. Der Effizienzgewinn wird meiner Ansicht nach nicht entstehen, weil die Methode hashCode() super schnell einen Hashcode generieren kann. Schneller als die Rückgabe von "0" geht kaum.

Es geht um die Effizienz beim Einfügen eines Buchs in ein HashSet: Thema "Buckets". Steht ja auch so da. Dazu sollte hashCode() für jedes einzelne Buch (außer Bücher sind gleich) einen unterschiedlichen Hashwert erzeugen.

EDIT: Siehe auch (wie immer) die Doku von HashSet:

Die Zeitmessung dazu musst du vermutlich auch nicht selbst schreiben. Die ist im Test enthalten, würde ich meinen.
Genau , man muss keine Zeitmessung schreiben ! Es ist viel einfacher ,als man denkt !:)
 

Neue Themen


Oben