javaseiten.de   |   Version 0.6
 

3.4. Beispiele: Einfache Collections

Mit zwei Beispielen sollen einfache Collections realisiert werden (Collections fassen eine Menge von Daten zu einer Einheit zusammen; die Klasse Vector<E> im Paket java.util ist z.B. eine Collection).

 

Beispiel 1: Einfache Liste

Es soll der generische Typ HolderList<E> erstellt werden, der eine Verkettung von einzelnen Elementen zu einer Liste ermöglicht. Dabei soll die Realisierung der Liste möglichst einfach gehalten werden. Durch Anhängen neuer Listenelemente kann sich die Länge der Liste erhöhen (ein Entfernen von Listenelementen ist nicht vorgesehen). Der Typparameter E legt dabei den Typ der Elemente fest, die in die Liste aufgenommen werden können. So können z.B. mit der Festlegung

HolderList<Integer> h1 = new HolderList<Integer>();

jeweils Integer-Objekte in diese Liste aufgenommen werden. Zu Beginn des Listings wird die Objektvariable value vom Typ des Typparamters deklariert, die später zur Laufzeit des Programms die Referenz des aufgenommenen Objekts speichert. Die zweite Objektvariable nextElement, deren Typ abhängig vom Typparamter E ist, ist eine Referenz auf das nächste Element in der Liste.

Abbildung 3.1. Verkettung von zwei Elementen zu einer einfachen Liste.

list2elements.jpg

Das Beispiel beinhaltet die Methode add, um ein Objekt in die Liste aufnehmen zu können, die Methode get, um ein Objekt an einer bestimmten Stelle innerhalb der List abfragen zu können, die Methode isEmpty, um festzustellen, ob das aktuelle Element der Liste besetzt ist und die Methode size, die die aktuelle Anzahl der Listenelemente ermittelt.

Listing 3.5. HolderList.java. Definition des generischen Typs HolderList<E>.

/* 
 * HolderList.java 
 * JDK 5
 *
 */

public class HolderList<E> {

  private E value;
  private HolderList<E> nextElement;

  public void add(E v) {
    if(isEmpty()) {                      // letztes Element?
      value = v;
      nextElement = new HolderList<E>(); // zusaetzliches Element anhaengen
    } else {
      System.out.print("a");
      nextElement.add(v);
    }  
  }

  public E get(int index) {
    if(index < 0 || isEmpty()) {
      throw new IndexOutOfBoundsException();
    } 
    if(index == 0) {
      System.out.print("r");
      return value;
    }  
    System.out.print("g");
    return nextElement.get(index - 1);
  }
  
  public boolean isEmpty() {
    return value == null;
  }

  public int size() {
    if(isEmpty()) {
      return 0;
    }  
    return 1 + nextElement.size();
  }
}

Der Aufruf der Methode size soll näher betrachtet werden. Durch den Aufruf von size zur Ermittlung der Anzahl der gespeicherten Objekte wird die komplette Liste bis zum letzten leeren Element durchlaufen. Der Aufruf von size des letzten Elements gibt den Zahlenwert 0 zurück. Alle vorhergenden Elemente liefern 1. Enthält eine Liste z.B. drei Objekte, dann würde sich der Rückgabewert der betrachteten Methode wie folgt addieren: return 1 + 1 + 1 + 0, d.h. nextElement.size() liefert zweimal 1 und einmal 0. Das nächste Listing verwendet HolderList<E>, indem der Typparameter konkret durch eine aktuelles Typargument ersetzt wird. Im zweiten Teil des Testprogramms wird der verwendete generische Typ ohne Angabe eines aktuellen Typarguments verwendet - es wird also auf den Raw Type HolderList zurückgegriffen. Das Programm beinhaltet zusätzliche Konsolenausgaben, um das vorkommende Durchlaufen der Liste zu verdeutlichen.

Listing 3.6. TestHolderList.java.

/* 
 * TestHolderList.java 
 * JDK 5
 *
 */

public class TestHolderList {

  public static void main(String[] args) {
    
    HolderList<Integer> h1 = new HolderList<Integer>();
    h1.add(new Integer(3)); System.out.println("3");
    h1.add(new Integer(4)); System.out.println("4");
    h1.add(new Integer(5)); System.out.println("5");
    h1.add(new Integer(6)); System.out.println("6");
    for (int j = 0; j < h1.size(); j++) {
      Integer i = h1.get(j);
      String name1 = i.getClass().getName();
      System.out.println(i.toString() + " (" + name1 + ")");
    }  
    
    System.out.println();
    
    // unchecked call to add(E)
    HolderList h2 = new HolderList();
    h2.add("Nummer");    System.out.println("Nummer"); 
    byte b = 9;          
    h2.add(new Byte(b)); System.out.println("9(1)");
    h2.add(b);           System.out.println("9(2)");
    for (int k = 0; k < h2.size(); k++) {
      Object obj = (Object)h2.get(k);
      String name2 = obj.getClass().getName();
      System.out.println(obj.toString() + " (" + name2 + ")");
    }  
  }  
}

Dabei wird durch das Testprogramm die folgende Ausgabe erzeugt:

3
a4
aa5
aaa6
r3 (java.lang.Integer)
gr4 (java.lang.Integer)
ggr5 (java.lang.Integer)
gggr6 (java.lang.Integer)

Nummer
a9(1)
aa9(2)
rNummer (java.lang.String)
gr9 (java.lang.Byte)
ggr9 (java.lang.Byte)

Zunächst werden drei Integer-Objekte in die Liste aufgenommen (die sogenannte Wrapper-Klasse Integer korrespondiert mit dem primitiven Datentyp int). Die aufgenommenen Objekte repräsentieren die Zahlenwerte 3,4,5 und 6. Die vierte Zahl 6 wird als letztes an die Liste angehängt und dabei wird über nextElement.add (bei jedem dieser Aufrufe wird ein "a" ausgegeben) solange die Liste durchlaufen, bis innerhalb der Methode festgestellt wird, dass das aktuelle Element noch nicht belegt ist, also der Ausdruck value==null nicht true ist. Ist dies der Fall, ist das letzte (nicht belegte) Element der Liste erreicht. Der Buchstabe "r" wird ausgegeben, wenn konkret innerhalb der get-Methode value zurückgegeben wird. Z.B. kann durch die Anweisung

Integer i = h1.get(3);

der Zahlenwert des Elements 3 ("6") abgefragt werden. Durch diese Anweisung wird dreimal nextElement.get aufgerufen, wobei das Argument der Methode get immer um eins reduziert wird:

Element 0: return nextElement.get(2);
Element 1: return nextElement.get(1);
Element 2: return nextElement.get(0);
Element 3: return value;

Ist das Argument schließlich bei "0" angelangt, erfolgt die eigentliche Rückgabe. Der zweite Teil der Konsolenausgabe resultiert aus der Verwendung des Raw Type HolderList.

 

Beispiel 2: Einfache Map

Der generische Typ Holder2List<K,V> soll definiert werden, um eine Zuordnung (Mapping) von Keys zu Values zu ermöglichen. Der Typ realisiert eine einfache verkettete Liste von Key-Value-Paaren (Map). Bei einer Map können Schlüssel nur einmal vorkommen, denn sie müssen eindeutig vergeben sein. Falls in der Map ein Key-Value-Paar aufgenommen werden soll und der Schlüssel bereits vorhanden ist, wird der alte Value durch den neuen ersetzt. Zwei Key-Value-Paare sollen z.B. wie folgt in die Map aufgenommen werden können:

Holder2List<String,Integer> h = new Holder2List<String,Integer>();
h.put("drei", new Integer(3)); 
h.put("vier", new Integer(4));

Der Schlüssel (key) wäre vom Typ String und der zugehörige Wert (value) vom Typ Integer. Die Methode put nimmt ein Paar in die Liste auf und mit get kann zu einem Schlüssel (wird als einziges Argument übergeben) der zugehörige Wert abgefragt werden. Die Definition des generischen Typs Holder2List<K,V> könnte wie folgt aussehen:

Listing 3.7. Holder2List.java. Definition des generischen Typs Holder2List<K,V>, der eine Zuordnung (Mapping) von Keys zu Values ermöglicht und eine einfache verkettete Liste von Key-Value-Paaren (Map) implementiert.

/* 
 * Holder2List.java
 * JDK 5
 *
 */

import java.util.*;

public class Holder2List<K,V> {
  
  private K key;
  private V value;
  private Holder2List<K,V> nextElement;

  public void put(K k, V v) {
    if(isEmpty()) {                         
      key = k;
      value = v;
      nextElement = new Holder2List<K,V>(); 
    } else {
      if (key == k) {
        value = v;
      } else {  
        nextElement.put(k, v);
      }  
    }  
  }

  public V get(K k) {
    if (key != null) {
      if (key == k) {
        return value;
      }
      return nextElement.get(k);
    }    
    return null;
  }
  
  public boolean isEmpty() {
    boolean b = ((key == null) && (value == null)); 
    return b;
  }

  public int size() {
    if(isEmpty()) {
      return 0;
    }  
    return 1 + nextElement.size();
  }

  
  public Enumeration<K> keys() {
    Enumeration<K> keyEnum = new Enumeration<K>() {
      int index = 0;
      int si = size();
      Holder2List<K,V> next = nextElement;
            
      public boolean hasMoreElements() {
        return index < si;
      }  
      
      public K nextElement() {
        if (index < si) {
          if (index == 0) { index++; return key; }
          if (index == 1) { index++; return nextElement.key; }
          next = next.nextElement;
          index++;
          return next.key;
        } else {
          throw new NoSuchElementException();
        }  
      }  
    
    };  
    return keyEnum;
  }

}

Die Definition des generischen Typs stellt auch die Methode keys zur Verfügung, deren Rückgabewert eine Aufzählung (Enumeration) aller vorhandener Schlüssel ist. Dazu wird das Interface Enumeration<E> implementiert. Zur Implementierung des Interfaces müssen die beiden Methoden hasMoreElements und nextElement ausgestaltet werden. Das folgende Listing verwendet Holder2List<K,V>. Es werden vier Key-Value-Paare in die Map aufgenommen, mit Hilfe von get wird zu einem Schlüssel der zugehörige Wert ermittelt, die Anzahl der Elemente innerhalb der Map wird mit size festgestellt und es werden alle vorhandenen Schlüssel der Map abgefragt und auf die Konsole ausgegeben.

Listing 3.8. TestHolder2List.java.

/* 
 * TestHolder2List.java 
 * JDK 5
 *
 */

import java.util.*;

public class TestHolder2List {

  public static void main(String[] args) {
    
    Holder2List<String,Integer> h = new Holder2List<String,Integer>();
    h.put("drei", new Integer(3)); 
    h.put("vier", new Integer(4)); 
    h.put("fuenf", new Integer(5));
    h.put("sechs", new Integer(6));
    
    String key = "vier";
    Integer i = h.get(key);
    if (i != null) {
      System.out.println("Value(" + key + "): " + i.toString());
    } else {
      System.out.println("Key \"" + key + "\" nicht vorhanden!");
    }
    
    System.out.println("Size: " + h.size());
    
    Enumeration<String> keyEnum = h.keys();
    System.out.print("Keys: ");
    while (keyEnum.hasMoreElements()) {
      key = keyEnum.nextElement();
      System.out.print(key + " ");
    }
    
  }  
}

Durch das Testprogramm wird die folgende Ausgabe erzeugt:

Value(vier): 4
Size: 4
Keys: drei vier fuenf sechs

 

 

 

Diese Seite nutzt Google-Dienste - siehe dazu Datenschutz.

Copyright © 2006, 2007 Harald Roeder