Advanced   Java   Services ArrayList und Vector Back Next Up Home

ArrayList und Vector als verallgemeinerte Arrays

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.


ArrayList versus Vector

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.


ArrayList

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.

Vector

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.


Typsichere ArrayList/Vector mit Hilfe von Generics

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.


Das klassische Problem, das zur Einführung von Generics geführt hat
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.


Der Vorteil von Generics

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.


Typisierte Behälterklassen

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.


Das obige Beispiel nun mit Hilfe von Generics
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.


Übungen

Übungen zu ArrayList/Vector

Valid XHTML 1.0 Strict top Back Next Up Home