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.
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>
.
public class HolderList<E> {
private E value;
private HolderList<E> nextElement;
public void add(E v) {
if(isEmpty()) {
value = v;
nextElement = new HolderList<E>();
} 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
.
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();
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.
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
.
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