Software-Entwurfsmuster

Filteriterator

Oft kommt es vor, dass beim Durchlaufen einer Folge mit einem Iterator nur diejenigen Elemente zurückgegeben werden sollen, die einer bestimmten Bedingung genügen. Die anderen Elemente sollen übersprungen werden sollen. Zum Beispiel soll eine ArrayList durchlaufen werden, wobei aber Einträge mit dem Wert null übersprungen werden.

Ein solcher Iterator ist ein Filteriterator. Ein Filteriterator benutzt einen Basisiterator (im Beispiel den normalen ArrayList-Iterator) und eine Filterfunktion, die dafür sorgt, dass nur die gewünschten Elemente zurückgegeben werden (im Beispiel also nur diejenigen Elemente, die nicht null sind).

Der Filteriterator ist nach dem Bridge-Entwurfsmuster gebildet. Er verwendet ein Behavior, das den Basisiterator und die Filterfunktion enthält sowie auch gleich noch eine Factory-Methode, um den so gebildeten Iterator zu erzeugen (Bild 1). Dieses Entwurfsmuster wird auch bei der Konstruktion eines Compound-Iterators verwendet.

 

Bild 1: Klassendiagramm eines Filteriterators mit dem konkreten Behavior NotNullElements 

Bild 1: Klassendiagramm eines Filteriterators mit dem konkreten Behavior NotNullElements

 

Nur die blau dargestellte Klasse NotNullElements ist vom Anwender zu schreiben (siehe nächster Abschnitt). Hierzu ist lediglich ein Basisiterator (hier der Standard-Iterator von ArrayList) anzugeben und eine Filterfunktion zu implementieren.

Die folgenden Programmbeispiele verwenden Typ-Parameter. Die benötigten Import-Anweisungen für Iterator und ArrayList sind der Kürze halber weggelassen.

Behavior NotNullElements

Ein Behavior, das einen Filteriterator erzeugt, wird von der abstrakten Klasse FilterIteratorBehavior abgeleitet. Diese sorgt dafür, dass ein Basisiterator und eine Filterfunktion vorhanden sind und stellt die Factory-Methode iterator zur Verfügung.

Auf Grundlage dieser abstrakten Klasse ist das Behavior NotNullElements gebildet. Es erzeugt einen Filteriterator, der eine ArrayList durchläuft und dabei null-Einträge überspringt. Im Konstruktor wird der Klasse die zu durchlaufende ArrayList übergeben. Dann werden die abstrakten Methoden der Basisklasse implementiert – die Methode baseIterator gibt den Basisiterator an und die Methode pass stellt die Filterfunktion dar.

Der Typ der ArrayList-Einträge wird zunächst durch den formalen Typ-Parameter Type dargestellt.

public class NotNullElements<Typeextends FilterIteratorBehavior<Type>
{
    private ArrayList<Type> a;

    public NotNullElements(ArrayList<Type> a)
    {
        this.a=a;
    }

    @Override
    // base iterator
    public Iterator<Type> baseIterator()
    {
        return a.iterator();    // standard iterator of ArrayList
    }

    @Override
    // filter function
    public boolean pass(Type x)
    {
        return x!=null;
    }

}

Anwendung

Die folgende Main-Funktion in der Klasse TestNotNullElements testet den Filteriterator. Sie gibt nur die Einträge "1", "2" und "3" aus.

public class TestNotNullElements
{
    public static void main(String[] args)
    {
        ArrayList<String> a=new ArrayList<String>();
        a.add(null);
        a.add("1");
        a.add(null);
        a.add("2");
        a.add(null);
        a.add(null);
        a.add("3");
        a.add(null);
 
        Iterator<String> it=new NotNullElements<String>(a).iterator();
        while (it.hasNext())
            System.out.print(it.next()+" ");
        System.out.println();

        // oder einfacher mit For-Each-Schleife
        for (String s : new NotNullElements<String>(a))
            System.out.print(s+" ");
        System.out.println();
    }
}

 

Es gibt auch die Möglichkeit, eine eigene Klasse NotNullIterator vorzusehen. Damit wird aus dem obigen Aufruf new NotNullElements<String>(a).iterator() mit der Factory-Methode iterator der einfachere Aufruf new NotNullIterator<String>(a).

Die entsprechende Klasse NotNullIterator ist wie folgt implementiert.

// eigener Iterator statt Factory-Methode iterator
public class NotNullIterator<Typeextends FilterIterator<Type>
{
    public NotNullIterator(ArrayList<Type> a)
    {
        super(new NotNullElements<Type>(a));
    }
}

 

Wenn es nur um Listen mit dem Datentyp String geht, lässt sich der Aufruf eines Iterators noch weiter zu new NotUndefStrIterator(a) vereinfachen, unter Verwendung der folgenden Klasse.

// eigener Iterator für Strings
public class NotUndefStrIterator extends FilterIterator<String>
{
    public NotUndefStrIterator(ArrayList<String> a)
    {
        super(new NotNullElements<String>(a));
    }
}

 

Zusammenfassung

Die konkret benötigte Funktionalität eines Filteriterators wird in einem Behavior (hier NotNullElements) festgelegt. Dies geschieht durch Angabe eines Basisiterators und einer Filterfunktion.

Zugrunde gelegt werden dabei getestete Implementierungen der Klassen FilterIterator und FilterIteratorBehavior.

 

Weiter mit:  [Compound-Iterator]   [Implementierung FilterIterator]   oder   [up]

 


H.W. Lang   mail@hwlang.de   Impressum   Datenschutz
Created: 05.02.2008   Updated: 18.02.2023
Diese Webseiten sind während meiner Lehrtätigkeit an der Hochschule Flensburg entstanden