Advanced Java Services | ArrayList und Vector |
ArrayList und Vector sind wichtige Vertreter der sogenannten Behälterklassen (Collection classes). Behälterklassen sind äußerst nützliche Verallgemeinerungen des Konzepts Array. Ein Array kann eine feste Anzahl von Objekten eines Typs aufnehmen. Der Zugriff auf die einzelnen Elemente erfolgt mit Hilfe eines Index. Eine Behälterklasse kann eine beliebige Anzahl von Objekten beliebigen Typs aufnehmen. Im Falle von ArrayList und Vector erfolgt der Zugriff auf die einzelnen Elemente ebenfalls mit Hilfe eines Index über eine get()-Methode.
In der Version 1.2 wird die Klasse ArrayList als Teil der CollectionsHierarchie eingeführt.
Die alte Klasse Vector wurde in diese Hierarchie mit eingebunden. Dies hat zur Folge, daß in der Klasse
Vector dieselben (oder ähnliche) Funktionalitäten unter verschiedenen Methodennamen doppelt auftauchen. So gibt es
in der Klasse Vector sowohl Methoden die eine Enumeration liefern als auch Methoden die einen Iterator liefern
da das Konzept Iterator auch erst in der Version 1.2 eingeführt wurde. Die Klasse ArrayList ersetzt wietgehend
weitgehend die Klasse Vector. Allerdings ist ArrayList (absichtlich) nicht threadsicher. In Anwendungen, wo nur
ein Thread auf das Objekt zugreift kann man so gefahrlos die performantere Klasse ArrayList einsetzen.
In der API heißt es zu Vector:
As of the Java 2 platform v1.2, this class was retrofitted to implement the List interface, making it
a member of the Java Collections Framework. Unlike the new collection implementations,
Vector is synchronized.
Dagegen liest man über ArrayList:
Note that this implementation is not synchronized. If multiple threads access an ArrayList instance concurrently,
and at least one of the threads modifies the list structurally, it must be synchronized externally.
(A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes
the backing array; merely setting the value of an element is not a structural modification.) This is typically
accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists,
the list should be "wrapped" using the Collections.synchronizedList method. This is best done at creation time,
to prevent accidental unsynchronized access to the list.
Für die Klasse ArrayList benötigt man das Import java.util.*;
Wichtige Konstruktoren | |
---|---|
ArrayList() |
Constructs an empty ArrayList so that its internal data array has size 10 and its standard capacity increment is zero. |
ArrayList(Collection c) |
Constructs a ArrayList containing the elements of the specified collection, in the order they are returned by the collection's iterator. |
Wichtige Methoden | |
---|---|
Returntyp | Name der Methode |
void |
add(int index, Object element) Inserts the specified element at the specified position in this ArrayList. |
boolean |
add(Object o) Appends the specified element to the end of this ArrayList. |
void |
clear() Removes all of the elements from this ArrayList. |
boolean |
contains(Object elem) Tests if the specified object is a component in this ArrayList. |
Object |
get(int index) Returns the element at the specified index. |
int |
indexOf(Object elem) Searches for the first occurence of the given argument, testing for equality using the equals method. |
Iterator |
iterator() Returns an iterator over the elements in this list in proper sequence (inherited from AbstractList). |
Object |
lastElement() Returns the last component of the ArrayList. |
Object |
remove(int index) Removes the element at the specified position in this ArrayList. |
Object |
set(int index, Object element) Replaces the element at the specified position in this list with the specified element. |
int |
size() Returns the number of components in this ArrayList. |
Object[] |
toArray() Returns an array containing all of the elements in this ArrayList in the correct order. |
Für die Klasse Vector benötigt man das Import java.util.*;
Wichtige Konstruktoren | |
---|---|
Vector() |
Constructs an empty vector so that its internal data array has size 10 and its standard capacity increment is zero. |
Vector(Collection c) |
Constructs a vector containing the elements of the specified collection, in the order they are returned by the collection's iterator. |
Wichtige Methoden | |
---|---|
Returntyp | Name der Methode |
void |
add(int index, Object element) Inserts the specified element at the specified position in this Vector. |
boolean |
add(Object o) Appends the specified element to the end of this Vector. |
void |
clear() Removes all of the elements from this Vector. |
boolean |
contains(Object elem) Tests if the specified object is a component in this vector. |
Object |
elementAt(int index) Returns the component at the specified index. |
Enumeration |
elements() Returns an enumeration of the components of this vector. |
Object |
get(int index) Returns the element at the specified index. |
int |
indexOf(Object elem) Searches for the first occurence of the given argument, testing for equality using the equals method. |
Iterator |
iterator() Returns an iterator over the elements in this list in proper sequence (inherited from AbstractList). |
Object |
lastElement() Returns the last component of the vector. |
Object |
remove(int index) Removes the element at the specified position in this Vector. |
int |
size() Returns the number of components in this vector. |
Object[] |
toArray() Returns an array containing all of the elements in this Vector in the correct order. |
Ein Vector ist ein im Hintergrund wachsendes dynamisches Array, in das man beliebige Objekte aufnehmen kann. Das folgende Beispiel nimmt einige Strings und einige StringBuffers mit add() in einen Vector auf und greift mit get() wieder auf diese zu. Bei diesem Vorgang erhält man allerdings Objekte vom Typ Objekt zurück und muß entsprechend casten. Im folgenden Beispiel will man nur die Elemente aus dem Vector haben, die Strings sind und überprüft das mit dem Operator instanceof.
import java.util.*; public class VectorDemo2 { public static void main(String[] args) { Vector v = new Vector(); v.add("Anna"); v.add("Luise"); v.add("Christine"); v.add("Agapi"); v.add( new StringBuffer("Thomas") ) ; v.add( new StringBuffer("Walter") ) ; v.add( new StringBuffer("Julian") ) ; v.add( new StringBuffer("Max") ) ; int len = v.size(); for(int i=0; i<len; i++) { Object ob = v.elementAt(i) ; if (ob instanceof String) System.out.println( (String)ob ); } } }
Die Methode elements() liefert ein Auflistung der Elemente des Vector als Typ Enumeration. Das Interface Enumeration wird als anonyme Klasse implementiert, wie ein Blick auf den Quellcode zeigt:
public Enumeration elements() { return new Enumeration() { int count = 0; public boolean hasMoreElements() { return count < elementCount; } public Object nextElement() { synchronized (Vector.this) { if (count < elementCount) { return elementData[count++]; } } throw new NoSuchElementException("Vector Enumeration"); } }; }
Der Nachfolger des Typs Enumeration ist das Interface Iterator. Die Methode iterator() erbt Vector von AbstractList. Im Unterschied zu Enumeration verfügt Iterator über eine zusätzliche Methode remove(), mit der man während des Iterierens ein Element aus der Liste entfernen kann.
Wie bereits erwähnt kann eine ArrayList bzw. ein Vector eine beliebige Anzahl von Objekten beliebigen Typs aufnehmen. Bei sehr viele Anwendungen werden aber nur Objekte ein- und desselben Typs aufgenommen (ähnlich wie bei einem Array). Beim Zugriff auf die aufgenommenen Objekte bekommt man aber immer Objekte mit dem basistyp Objetct zurückgeliefert. Also wird man einen cast verwenden (müssen) um mit den Methoden der aufgenommenen Objekte zu arbeiten. das folgende Beispiel soll dies erläutern.
ArrayList list = new ArrayList(); list.add("Adam"); list.add("Eva"); list.add("Philemon"); list.add("Baucis"); for(int i=0; i<list.size(); i++) { String name = (String)list.get(i); // cast ! System.out.println( name.length() ); }
Hier klappt noch alles. Wir casten die erhaltenen Objekte auf den Typ String (wir wissen, es sind Strings !) und nur so können wir in der Schleife die Länge der einzelnen Strings ausgeben. Die Klasse Object besitzt ja keine Methode length().
ArrayList list = new ArrayList();
list.add("Adam");
list.add("Eva");
list.add("Philemon");
list.add("Baucis");
list.add( new StringBuilder("Daphnis") );
list.add( new StringBuilder("Chloe") );
for(int i=0; i<list.size(); i++)
{
String name = (String)list.get(i); // cast und class cast exception
System.out.println( name.length() );
}
Hier hat jemand nicht aufgepaßt und zwei StringBuilderobjekte aufgenommen. In der Schleife kommt es daher zu einem Laufzeitfehler, einer RuntimeException, wogegen der Compiler keinen Fehler meldet. Er hat eben grundsätzliche keine Möglichkeit einen Laufzeitfehler beim Compilieren festzustellen.
Mit Hilfe von Generics gibt man dem Compiler jedoch die Möglichkeit solche Fehler zu entdecken. Damit werden aus Laufzeitfehlern Compilerfehler. Und je mehr Fehler man beim Complieren entdeckt umso weniger Laufzeitfehler gibt es.
Durch eine neu eingeführte Syntax (sie ähnelt der von Templates in C++) kann man seit Java 1.5 anzeigen, daß man etwa ein ArrayListobjekt nur für einen Typ oder für eine Gruppe von Typen generieren will. Beim Erstellen eines Containerobjekts gibt man an, welcher Elementtyp gespeichert werden soll. Die neue Syntax ermöglicht so eine Überprüfung zur Compilezeit und ist damit wesentlich weniger fehleranfällig als der nachträgliche cast, der nun überflüssig wird.
ArrayList<String> list = new ArrayList<String>(); // Arraylistobjekt für Strings typisieren list.add("Adam"); list.add("Eva"); list.add("Philemon"); list.add("Baucis"); for(int i=0; i<list.size(); i++) { String name = list.get(i); // kein cast notwendig System.out.println( name.length() ); }
Mit Hilfe der for each Schleife wird das noch einfacher:
for( String name: list) // in list sind nur Strings !
{
System.out.println( name.length() );
}
Und falls wir jetzt andere Objekte als Stringobjekte aufnehmen wollen, dann klopft uns der Compiler auf die Finger:
ArrayList<String> list = new ArrayList<String>(); // Arraylistobjekt für Strings typisieren list.add("Adam"); list.add("Eva"); list.add("Philemon"); list.add("Baucis"); list.add( new StringBuilder("Daphnis") ); // Error at compiletime: cannot find symbol : method add(java.lang.Integer) list.add( new StringBuilder("Chloe") ); for( String name: list) { System.out.println( name.length() ); }
Der Compiler weiß nun, daß list nur Stringobjekte aufnehmen darf und paßt die Signatur der Methoden entsprechend an. Die add-Methode etwa hat nun nicht mehr die Signatur add(Object) sondern add(String). Daher die Fehlermeldung wenn wir versuchen ein StringBuilderobjekt aufzunehmen.