Zum Menü springen Zum Inhalt springen

iamBlog

Java: Generische Datenstrukturen & Object

15.04 2008 15:52

So. Schon wieder ein Java-Eintrag ;) In der neuen Aufgabe müssen wir unser altes Progrämmchen wieder ein bisschen erweitern. In Schritt A soll eine Klasse "Vektor" programmiert werden, von der dann eine weitere Klasse "Set" abgeleitet wird. Schritt B verlangt von uns, dass wir die Methode equals der Klasse Artikel überschreiben. In C wird unsere Klasse Videothek überarbeitet. Nun muss der neue Code laut D in einer Testsuite - wer hätt's gedacht - getestet werden ;)

A:

Die Klasse Vektor: Unser Vektor soll Objekte in einem Array verwalten und die Anzahl der gespeicherten Elemente in einer Variablen speichern.


public class Vektor {
    private Object[] object;
    private int length = 0;
}

noch den Konstruktor hinzufügen:


public Vektor(int initialCapacity){
    this.object = new Object[initialCapacity];
}

und nun ran an die Methoden:

void add( Object element ) - Fügt eine neues Element hinzu. Ist das interne Array voll, dann wird es automatisch vergrössert. Diese Angabe verrät uns schon fast alles, was zu tun ist:

Diese add-Methode fügt einfach an der Position length im Array das neue Element ein:


public void add(Object element){    
    object[length] = element;
    length++;
}

Dies wirft aber folgende Probleme auf: Was ist, wenn length größer als die Länge von object ist? Wie lassen wir unser object dynamisch wachsen?

Diese erweiterte add-Methode überprüft nun ob die Anzahl der Elemente in object an ihrem "Limit" ist wenn dies der Fall ist wird das Array object um 1 erweitert.


public void add(Object element){
    if(object.length == length){
        Object [] tmp = new Object[length + 1];
        for(int c = 0; c < length; c++){
            tmp[c] = object[c];
        }
        object = tmp;
    }
        
    object[length] = element;
    length++;
}

Hier noch eine überlagerte Version der add-Methode, die später benötigt wird. Sie überschreibt einfach das Element an der Position index mit dem Objekt element:


    public void add(Object element, int index){
        object[index] = element;
    }

void remove( int index ) Entfernt das übergebene Element aus der Datenstruktur

Die rem-Methode löscht dann einfach an der Position index im Array das Element wieder raus:


public void rem(int index){
    if(index >= 0 && index < object.length){
        object[index] = null;
        length--;
        
        Object[] tmp = new Object[object.length];
        int cnt = 0;
        for(int c = 0; c < object.length; c++){
            if(object[c] != null){
                tmp[cnt++] = object[c];
            }
        }
        object = tmp;
    }
}

Löschen wird in diesem Fall so gelöst, dass das zu löschende Element auf "null" gesetzt wird. Danach wird die Anzahl der Elemente length in unserem Objekt-Array um 1 verringert. (wurde ja gerade gelöscht) Anschließend werden die Elemente im Array "nach Vorne" gebracht (for-Schleife), das hat den tollen Effekt, dass man in dem Array mit length arbeiten kann, also nicht extra die ganzen Elemente durchgehen und überprüfen muss, ob diese nun nicht null oder eben null sind.

Object get( int i ) Liefert das Objekt welches an Index i des internen Arrays abgespeichert ist

Die get-Methode liefert das Element des Arrays object an der Position index:


    public Object get(int index){
        Object ret = null;
        if(index >= 0 && index < length){
            ret = object[index];
        }
        return ret;
    }

Diese Methode beschreibt beim Aufruf die "Rückgabevariable" ret mit null und weißt dieser, dann ein Objekt aus dem Array zu. (wenn sich das Element im gültigen Index-Bereich befindet - darum auch die Sortierung in der rem-Methode)

int getLength() Liefet die aktuelle Grösse (Anzahl gespeicherter Elemente) zurück

Die getLength-Methode (eine sog. getter-Methode) gibt einfach den Wert von length zurück:


    public int getLength(){
        return length;
    }

Soweit zu der Klasse Vektor.

Nun widmen wir uns ihrer Kind-Klasse - oder wie sie in der Aufgabe genannt wird: Unterklasse - Set.

Die Klasse Set:

Hier die leere Klassenhülle, mit extends und dem Konstruktor: (extends zeigt, dass wir es mit einer Kind-Klasse zu tun haben - in diesem Fall "Vektor")


public class Set extends Vektor{
    public Set(int initialCapacity) {
        super(initialCapacity);
    }
}

Erweitern Sie die Klasse Set um eine Methode "int contains( Object obj)". Diese Methode prüft ob das übergebene Objekt bereits in der Datenstruktur enthalten ist (Stichpunkt "equals"). Falls ja, liefert die Methode den Index innerhalb des internen Arrays. Andernfalls liefert die Methode -1 zurück

Die Methode contains() soll prüfen, ob es das übergebene Objekt gibt, oder nicht:


    public int contains(Object obj){
        int ret = -1;
        
        for(int c = 0; c < super.getLength(); c ++){
            if(obj != null){
                if(super.get(c).equals(obj)){
                    ret = c;
                    break;
                }
            }
        }
        return ret;
    }

Zum Code: Zuerst wird der Rückgabewert der Methode, ret, auf -1 gesetzt, da wir einfach mal annehmen, dass kein Objekt gefunden wird, das den Suchkriterien entspricht. Falls dies nicht der Fall ist und wir von den 2 if-Bedingungen eines Besseren belehrt werden, wird dank dieser Erkenntnis sofort ret mit diesem "Treffer" assoziiert und aus der Schleife gesprungen. (es gibt ja kein 2. Element, das den Kriterien entsprechen könnte - dafür sorgt diese Methode ja schon beim "adden" der anderen Elemente)

In der Aufgabenstellung sind die "todo-Infos" für die Methoden add und contains genau umgedreht. Aus Gründen der Einfachheit (und weil man contains in add schön verwenden kann ;) ) werden die Methoden in hier beschriebener Reihenfolge "erklärt".

Überschreiben Sie die Methode add so, dass beim Einfügen geprüft wird, ob das neu einzufügende Objekt bereits vorhanden ist oder nicht. Verwenden Sie zur Prüfung auf Gleichheit die Methode "equals".

Die add-Methode schreibt das Object element in das Array:


    public void add(Object element){
        
        int cont = contains(element);
        if(cont == -1){
            super.add(element);
        } else {
            super.add(element, cont);
        }
    }

Da wir vorhin die schöne contains-Methode geschrieben haben, wollen wir ihr hier eine erste Aufgabe geben. In dieser überlagerten add-Methode (gab es auch schon in der Vaterklasse von Set: Vekor) wird das Objekt element zum Array hinzugefügt, (wenn cont == -1) oder ein schon bestehendes Element (else) überschrieben. Die add-Methoden die dazu verwendet werden, werden über super von der Vaterklasse Vektor "geholt".

B:

Überschreiben Sie die Methode equals für die von Ihnen implementierten "Artikel"-Klassen. Hierbei muss folgende Bedingung für die Gleichheit gelten:

Zwei Artikel sind gleich, wenn es sich um ein und dasselbe Objekt handelt => das Objekt, welches equals anwendet == obj (in codeform: this == obj)

Zwei Artikel sind gleich wenn Sie die gleiche Artikelnummer und den gleichen Artikeltyp haben ( z.B. CD, DVD, ....) => artNr und artTyp von diesem Objekt == artNr und artTyp von obj (in codeform: this.getArtikelNummer() == ((Artikel) obj).getArtikelNummer() && this.artikelTyp == ((Artikel)obj).getArtikelTyp())

Dann einfach diese beiden Erkenntnisse in eine if-Bedingung quetschen und schon hat man die halbe equals-Methode:


public boolean equals(Object obj){
    boolean ret = false;
        if(obj != null){
            if(this == obj || this.getArtikelNummer() == ((Artikel) obj).getArtikelNummer() &&  this.artikelTyp == ((Artikel)obj).getArtikelTyp()){
                ret = true;
            }
        }
        return ret;
    }

Dass es eine ganze equals-Methode wird, fügen wir noch die "Rückgabevariable" ret an, die initial (vom Anfang an) auf false gesetzt ist. Wenn unser obj laut den Kriterien == dem Objekt ist, das equals aufruft, wird ret auf true gesetzt. Das return ret das ganze zurückgibt muss ich ja nicht extra aufführen. ;)

C:

Videothek überarbeiten...

In dieser Aufgabe müsst ihr eure bereits vorhandene Klasse Videothek und ggf. die Artikel anpassen.

Ihr Auftraggeber möchte in Zukunft alphanumerische Artikelnummern (z.B. "DVD00012234" ) verwenden. Das heist für Sie, dass die Artikelnummern in Zukunft durch den Datentyp String repräsentiert werden. Führen Sie die notwendigen Änderungen in Ihrem Programm durch.

Zuerst werden die Artikelnummern von int auf String geändert. Wenn ihr dies einfach Kopf-durch-die-Wand ändert - meckert Eclipse und zeigt mit seinen wunderschönen (:D) roten Kreuzchen was jetzt nicht so sein sollte, wie es gerade ist.

Dies muss aber dann von euch angepasst werden.

Als Beispiel der Anfang der Klasse Artikel könnte so aussehen:


package videothek.article;

public class Artikel
{
    private String  artikelName;
    private String  artikelNummer;
    private String  artikelBeschreibung;
    private int     availableArticles;
    private String  artikelTyp;

    public Artikel(String artikelName, String artikelNummer, String artikelBeschreibung, int availableArticles, String artikelTyp)
    {
        this.artikelName            = artikelName;
        this.artikelNummer          = artikelNummer;
        this.artikelBeschreibung    = artikelBeschreibung;
        this.availableArticles      = availableArticles;
        this.artikelTyp             = artikelTyp;
    }

    ...

}

Ändern Sie Ihre Klasse Videothek so ab, dass nun für die Ablage der Artikel die Klasse "Set" verwendet wird. Passen Sie die bereits implementierten Methoden entsprechend an. Insbesondere die Methode addArticle.

Die neue add-Methode der Klasse Videothek:


    public void addArticle(Artikel article)
    {
        article.setArtikelNummer(generateArticleNumber(article.getArtikelTyp()));
        artikelliste.add(article);
    }

Man sieht hier schön, was uns unsere Klassen Vektor und Set gebracht haben ;)

Ähnlich sieht es bei der Methode deleteArticle aus:


    public void deleteArticle(String articleNr)
    {
        for(int c = 0; c < artikelliste.getLength(); c++){
            if(artikelliste.get(c).equals(findArticle(articleNr))){
                artikelliste.rem(c);
                break;
            }
        }
    }

Erweitern Sie Ihre Klasse "Videothek" um eine Methode "Article findeArtikel( String ArtikelNummer)". Diese Methode versucht einen Artikel über dessen Artikelnummer zu finden. Ist der Aritkel vorhanden wird direkt eine Referenz auf das zugehörige Artikel-Objekt zurückgeliefert. Andernfalls "null". Bauen Sie diese Methode an jeder Stelle in Ihrer Viedeothek ein welche den Zugriff auf Artikel über die Artikelnummer benötigt.

Die Methode findArticle gibt uns das Objekt zurück, das hinter der übergebenen Artikelnummer steht - oder wenn keine Nummer gefunden wird: null


    public Artikel findArticle(String articleNr){
        Artikel ret = null;
        for(int c = 0; c < artikelliste.getLength(); c++){
// <überarbeitet>
            if(((Artikel) artikelliste.get(c)).getArtikelNummer().equalsIgnoreCase(articleNr)){
// 
                ret = (Artikel) artikelliste.get(c);
                break;
            }
        }
        return ret;
    }

Um eine gewisse "Systematik" in der Aritkelnummervergabe zu garantieren bittet Sie Ihr Auftraggeber eine Methode "generateArticelNumber( String artickleType )" zu erstellen. Diese Methode erzeugt eine Artikelnummer indem zufällig generierte Artikelnummern nach folgendem Muster erstellt werden: Artikeltyp + 12-stellige Nummer. (z.B. DVD000000471100 ). Zum erstellen von zufüalligen Nummer können Sie die Methode java.util.Random.nextInt() verwenden. Bitte berücksichtigen Sie bei diesem Verfahren dass es natürlich vorkommen kann, dass die erzeugte Artikelnummer bereits vergeben ist. Behandeln Sie diesen Fall entsprechend.

Die generateArticleNumber-Methode liefert eine generische Artikelnummer bestehend aus: Artikeltyp und einer 12-stelligen zufälligen Zahlenkombination der Ziffern 0 bis 9:

Konkret schaut das dann so aus:


public String generateArticleNumber(String artType){
    String ret = artType;
    for(int c = 0; c < 12; c++){
        ret += (int) (Math.random() * 9);
    }

    if(findArticle(ret) != null){

// <überarbeitet>
        ret = generateArticleNumber(artType);
// 

    }
    return ret;
}

Jaaa ihr seht richtig. Diese Methode ist sogar rekursiv! Falls der Fall eintritt, dass Artikeltyp und die 12-stellige Zufallszahl ein 2. mal vorkommen, wird alles so lange noch einmal gemacht, bis die generierte Artikelnummer einzigartig ist. (bei einer 12 stelligen Zahl, die pro Stelle 10 Möglichkeite besetzungen hat... naja aber sicher ist sicher ;) )

D:

Implementieren Sie eine Testsuite welche die Anwendung der oben definierten Methoden/Änderungen demonstriert.

So. Fast am Ende.

Nur noch den ganzen Code testen, den wir "gerade" produziert haben:


import videothek.*;
import videothek.article.*;

public class testsuite {

    public static void main(String[] args) {
        Videothek video = new Videothek();

        CD article1 = new CD("Pink Floyd","Pink Floyd, Another Brick in the Wall", 2);
        video.addArticle(article1);

        DVD article2 = new DVD("Dinofilm","In Einem Land vor unserer Zeit", 0, 0);
        video.addArticle(article2);

        SWEET article3 = new SWEET("Kaugummi","Kirsch-Geschmack", 20, "5Tage");
        video.addArticle(article3);

        DVDPlayer article4 = new DVDPlayer("DVDPlayer", "Dies ist ein Gerät zur Wiedergabe von Filmen", 2);
        video.addArticle(article4);
        video.listArtikel();

        
        video.addArticle(article1); // Schlecht
        video.listArtikel();
        
        video.buyArticle(article3.getArtikelNummer()); // Gut
        video.buyArticle(article4.getArtikelNummer()); // Schlecht
        video.listArtikel();
        
        video.borrowArticle(article1.getArtikelNummer()); // Gut
        video.borrowArticle(article3.getArtikelNummer()); // Schlecht
        video.listArtikel();
        
        video.deleteArticle(article4.getArtikelNummer()); // Gut
        video.deleteArticle("nichtvorhandenerartikel"); // Schlecht
        video.listArtikel();
    }
}

Fertig. Bitte nehmt's mir nicht übel, dass ich zum Schluss hin mit meinen Erklärungen knapper wurde, aber es ist halt doch tierisch große Arbeit so 'nen Java-Beitrag zu schreiben. Ich hoffe, dass es trotzdem verständlich genug ist ;) Danke fürs lesen =)

Kommentare

  1. 1

    Hallo miteinander,

    von meiner Seite aus ein kleiner Tipp. Im obigen Beispiel wird der Array immer um dies Größe "1" erweitert. D.h. mit jedem Artikel der neu eingefügt wird muss eine komplette Kopie erzeugt werden. Dies ist natürlich für eine "kleine" Videothek gut machbar. Werden aber viele Artikel eingefügt kann dies sehr viel Zeit und Speicher kosten. Aus diesem Grund würde ich vorschlagen die Größe des Arrays jeweils zu verdoppeln. Dadurch wächst die Größe sehr schnell und überflüssige Operationen werden vermieden.

    Generell: Das Kriterium an ein gutes Algorithmendesign muss lauten: Welche Qualitätskriterien müssen für die Software gelten. Ist es Performance oder geringe Speicherverbrauch? Wenn man diese Frage beantwortet dann kann man auch entscheiden welche Lösung die bessere ist.

    Kommentar von Dr. Bernd Hafenrichter am 16. 04. 2008 um 16:16 Uhr
  2. 2

    Grüzi, bei:

    if(findArticle(ret) != null){ generateArticleNumber(artType); }

    fehlt ein return ;) Das ganze rekursive bringt nix, wenn du den Wert ned abfängt.

    also if(findArticle(ret) != null){ return generateArticleNumber(artType); }

    Kommentar von Rainer am 17. 04. 2008 um 14:27 Uhr
  3. 3

    Hallo zusammen, möchte auch noch ein kleine Anmerkung machen:

    Bitte Strings nie mit "==" vergleichen.

    z.B. if(((Artikel) artikelliste.get(c)).getArtikelNummer() == articleNr)

    Das geht zwar meistens gut, weil ja "==" die Referenzen prüft und 2 gleiche Strings einer Klasse auf das gleiche Objekt referenzieren, aber wenn man z.B. Strings über die Tastatur einliest oder zusmmensetzt geht das nicht mehr :( Also bei Strings immer .equals() .equalsIgnoreCase() oder .compareTo() benutzen.

    Wer's nicht glaubt kann folgendes Beispiel mal ausprobieren:

    
             String meinString = "MeinString";
            String mein = "Mein";
            String string  = "String";
            String meinString2  = mein + string;
    
            System.out.println("meinString:  " + meinString);
            System.out.println("meinString2: " + meinString2);
            
            
            if (meinString == meinString2) {
                System.out.println("Sollte doch eigentlich angezeigt werden ...");
            }
            
            if (meinString.equals(meinString2)) {
                System.out.println("Equals ROCKXXX");
            }
    

    Sonstige pitfalls besprechen wir dann im Praktikum. Ansonsten Lob für das ausführliche Tutorium - ich hoffe ihre Kommilitonen spendieren ihnen dafür das eine oder andere Mittagessen ...

    Viele Grüße

    Kommentar von Gregor "Besserwisser-Bob" Liebermann am 17. 04. 2008 um 15:00 Uhr
  4. 4

    Man sollte hier wirklich "==" ersetzen, da es zwas oft funktioniert aber gerade hier nicht :D (dürfte in der findArticle-Methode sein)

    @ Rainer

    Es war tatsächlich ein Fehler in der Methode. Allerdings lag es nicht am return. Das Problem war weit simpler:

    @ all

    Ihr solltet die Zeile generateArticleNumber(artType); mit dieser ersetzen: ret = generateArticleNumber(artType); Ich hatte das irgendwie übersehen, dass diese Methode ja einen String zurückgibt. In der vorigen Version würde einfach die Methode aufgerufen, aber der ursprüngliche Wert zurückgegeben werden. Jetzt funktioniert es aber richtig und ret ist immer unterschiedlich. Dies ist mir leider erst jetzt aufgefallen, da der Zufall immer alles passend hingedreht hat ;)

    
        public String generateArticleNumber(String artType){
            String ret = artType;
            for(int c = 0; c < 12; c++){
                ret += (int) (Math.random() * 9);
            }
            if(findArticle(ret) != null){
                ret = generateArticleNumber(artType);
            }
            return ret;
        }
    
    Kommentar von Dominik Felber am 17. 04. 2008 um 15:59 Uhr
  5. Dieser Eintrag kann nicht mehr kommentiert werden.

Nächster Eintrag: My Designer of the Week #1: The Co-Op

Vorheriger Eintrag: Das Weltfernsehen von Adobe kommt!

Über den iamBlog

Der iamBlog ist von und für Studenten der FH Augsburg und alle Leute die sich für Design, Medien und Multimedia interessieren.

Namensgebend war der Studiengang InterAktive Medien an der FH Augsburg.

Der Blog befindet sich Moment noch im Aufbau. Wir entschuldigen uns für eventuell fehlende Funktionen und den ein oder anderen Fehler im System.

Die neuesten Links

Kategorien

Das neueste Design

Avant von pichfl

Zum Auswählen eines Designs einfach oben links auf den Button Hintergrund ändern klicken.

Feeds

Die Einträge im Blog lassen sich als Atom-Feed abonnieren. Außerdem gibt es einen kombinierten Atom-Feed, der Blogeinträge und Links enthält.
Diese Feeds werden z.B. von Browsern wie Safari oder Mozilla Firefox, Newsreadern wie NetNewsWire oder Feadreader oder Onlinediensten wie Google Reader unterstützt und werden automatisch aktualisiert, wenn neue Einträge im iamBlog vorhanden sind.

Werbung

Disclaimer

This page will never work with Internet Explorer. If you use Internet Explorer to view this page, all styles and features are deactivated. Use Firefox, Safari or Opera to see all the beautiful colours of the internet.