Listendublette bei rekursivem Komponentenaufruf

innoc

Aktives Mitglied
Ich habe da ein Problem bei der Zerlegung eines Textes in seine einzelnen Komponenten. Für die Zerlegung sollen Komposit- und Komponenten-Klassen verwendet werden. Ich habe mir dazu folgenden Lösungsansatz ausgedacht. Zuerst zerlege ich den Text in die einzelnen Sätze. Mit dem Punkt als Satztrenner ist das ja kein Problem. Die Sätze speichere ich als Elemente in einer Arrayliste. Als nächstes benutze ich das Leerzeichen, um die einzelnen Wörter eines Satzes in ein Stringarray zu extrahieren. Das hat auch noch geklappt. Was mich jedoch verwundert ist, dass bei der Ausgabe der Komponenten, also den Wörtern im Stringarray, über eine Methode getText() abhängig von einer Laufvariablen die Ausgabe der Komponenten dubliziert wird. Bei der Ausgabe der Komposits passiert das nicht, wodurch ich vermute, dass etwas beim Aufruf und der Definition der Methode getText() schief läuft. Stört da vielleicht, dass ich sowohl in der Klasse Clients, als auch in der Klasse Kompositum über die ChildComponents in einer foreach-Schleife iteriere? Wenn da der Fehler liegt, welche der beiden foreach-Schleifen kann ich mit was ersetzen?
Anbei die relevanten Klassen:
TextChild.java:
Java:
public interface TextChild {
  void setText(String text);
  String getText();
}

Component.java
Java:
class Component implements TextChild {
  String text;

  public void setText(String text) {
    this.text = text;
  }

  public String getText() {
    return text;
  }

}

Kompositum.java
Java:
import java.util.*;

class Kompositum {
  String text;

  // hier: Components als Liste vorgehalten
  public List<TextChild> childComponents = new ArrayList<TextChild>();


  // rekursiver Aufruf auf childComponents
  public String getText() {
    for (TextChild childComps : childComponents) {
      childComps.getText();
    }
    return text;
  }

  // Überschreiben der Default-Implementierung

  public void add(TextChild pComp) {
    childComponents.add(pComp);
  }

  public void remove(TextChild pComp) {
    childComponents.remove(pComp);
  }

  public TextChild getChild(int pIndex) {
    return childComponents.get(pIndex);
  }

}

Client.java (enthält die main-Methode)
Java:
import java.util.*;

public class Client {
  public static ArrayList<String> al = new ArrayList<String>();
  public final static String text = "Der Hamster liebt das Rad. Der Hund begehrt die Wurst.";

  public static Component subjectArticle = new Component();
  public static Component subjectNomen = new Component();
  public static Component verb = new Component();
  public static Component objectArticle = new Component();
  public static Component objectNomen = new Component();

  public static Kompositum kompositum = new Kompositum();


  public static void main (String[] args) {

    // This is the block of text to process.
    String block = text;

    // Loop over all sentences.
     while (!block.equals("")) {
      // Remove leading spaces
      if (block.startsWith(" ")) {
        block = block.substring(1);
        continue;
      }
      // Find end of sentence
      int end = block.indexOf('.');
      // Extract sentence and remove it from the text block
      String sentence = block.substring(0, end);
      al.add(sentence);
      block = block.substring(end+1);
    }

    System.out.println(al);


    Iterator itr = al.iterator();
    while (itr.hasNext()) {
      Object element = itr.next();
      // Split sentence at whitespace into array
      String[] stringarr = ((String)element).split("\\s+");

      // Process each starting word.
      for (int i = 1; i < stringarr.length; i++) {  
        subjectArticle.setText(stringarr[0]);
        subjectNomen.setText(stringarr[1]);    
        verb.setText(stringarr[2]);
        objectArticle.setText(stringarr[3]);
        objectNomen.setText(stringarr[4]);
      }
      kompositum.add(subjectArticle);
      kompositum.add(subjectNomen);
      kompositum.add(verb);
      kompositum.add(objectArticle);
      kompositum.add(objectNomen);
      // Einfache Nutzung der Struktur
      for (TextChild childComps : kompositum.childComponents) {
        System.out.println("" + childComps.getText());
      }

      ArticlePlusNomen subject = new ArticlePlusNomen(subjectArticle, subjectNomen);
      ArticlePlusNomen object = new ArticlePlusNomen(objectArticle, objectNomen);
      Sentence sen = new Sentence(subjectArticle, subjectNomen, verb, objectArticle, objectNomen);


      System.out.println("" + subject.getList());
      System.out.println("" + object.getList());
      System.out.println("" + sen.getList());


    }
    


  }

}

Weniger relevant für das Problen, denn mit den Komposite-Derivaten klappt alles, aber zur Vollständigkeit noch die Klassen

ArticlePlusNomen.java
Java:
import java.util.*;

class ArticlePlusNomen extends Kompositum {
  private TextChild _article, _nomen;

  private List<String> al = new ArrayList<String>();

  ArticlePlusNomen(TextChild article, TextChild nomen) {
    _article = article;
    _nomen = nomen;
  }

  // rekursiver Aufruf auf childComponents
  public List<String> getList() {
    al.add(_article.getText());
    al.add(_nomen.getText());
    return al;
  }

}

und Sentence.java
Java:
import java.util.*;

class Sentence extends Kompositum {
  private TextChild _articlesubj, _nomensubj, _verb, _articleobj, _nomenobj;

  private List<String> al = new ArrayList<String>();

  Sentence(TextChild articlesubj, TextChild nomensubj, TextChild verb, TextChild articleobj, TextChild nomenobj) {
    _articlesubj = articlesubj;
    _nomensubj = nomensubj;
    _verb = verb;
    _articleobj = articleobj;
    _nomenobj = nomenobj;
  }

  // rekursiver Aufruf auf childComponents
  public List<String> getList() {
    al.add(_articlesubj.getText());
    al.add(_nomensubj.getText());
    al.add(_verb.getText());
    al.add(_articleobj.getText());
    al.add(_nomenobj.getText());
    return al;
  }

}

Als Ausgabe erhalte ich:

[Der Hamster liebt das Rad, Der Hund begehrt die Wurst]
Der
Hamster
liebt
das
Rad
[Der, Hamster]
[das, Rad]
[Der, Hamster, liebt, das, Rad]
Der
Hund
begehrt
die
Wurst
Der
Hund
begehrt
die
Wurst

[Der, Hund]
[die, Wurst]
[Der, Hund, begehrt, die, Wurst]

Das was weg soll habe ich fett markiert. Ich gebs zu ist ziemlich viel Programmcode, aber vielleicht steigt da ja jemand durch und hat eine Idee, wie ich den Fehler beheben kann.
 

innoc

Aktives Mitglied
Danke für die Fülle an Antworten. Ich habe diese sch... Iteratoren auf den Listen ersetzt bzw. die Elemente in dem ListenArray durch das Übertragen auf einen LinkedHashSet von den doppelten Elementen befreit. Für den Fall, dass es jemand da Draußen interessiert, anbei die geänderten Klassen:

In Sentence.java hab ich die LinkedHashSet als Filter eingefügt

Java:
import java.util.*;

class Sentence extends Kompositum {
  private TextChild _verb;
  private ArticlePlusNomen _subject, _object;

  private List<String> al = new ArrayList<String>();


  Sentence(ArticlePlusNomen subject, TextChild verb, ArticlePlusNomen object) {
    _verb = verb;
    _subject = subject;
    _object = object;
  }

  // rekursiver Aufruf auf childComponents
  public List<String> getList() {
    al.addAll(_subject.getList());
    al.add(_verb.getText());
    al.addAll(_object.getList());
    LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>(al);
    al.clear();
    al.addAll(linkedHashSet);
    return al;
  }

}

In Client.java habe ich den Iterator durch eine Iteration mit einer Laufvariablen ersetzt (nicht schön, aber dafür funktioniert es jetzt)
Java:
import java.util.*;

public class Client {
  public static ArrayList<String> al = new ArrayList<String>();
  public final static String text = "Der Hamster liebt das Rad. Der Hund begehrt die Wurst. Der Vater liest die Zeitung.";

  public static Component subjectArticle = new Component();
  public static Component subjectNomen = new Component();
  public static Component verb = new Component();
  public static Component objectArticle = new Component();
  public static Component objectNomen = new Component();

  public static Kompositum kompositum = new Kompositum();


  public static void main (String[] args) {

    // This is the block of text to process.
    String block = text;

    // Loop over all sentences.
     while (!block.equals("")) {
      // Remove leading spaces
      if (block.startsWith(" ")) {
        block = block.substring(1);
        continue;
      }
      // Find end of sentence
      int end = block.indexOf('.');
      // Extract sentence and remove it from the text block
      String sentence = block.substring(0, end);
      al.add(sentence);
      block = block.substring(end+1);
    }

    for (Iterator itr = al.iterator(); itr.hasNext();) {
      Object element = itr.next();
      // Split sentence at whitespace into array
      String[] stringarr = ((String)element).split("\\s+");
      subjectArticle.setText(stringarr[0]);
      subjectNomen.setText(stringarr[1]);    
      verb.setText(stringarr[2]);
      objectArticle.setText(stringarr[3]);
      objectNomen.setText(stringarr[4]);
      kompositum.add(subjectArticle);
      kompositum.add(subjectNomen);
      kompositum.add(verb);
      kompositum.add(objectArticle);
      kompositum.add(objectNomen);
      for (int i = 0; i < stringarr.length; i++) {
        System.out.println(kompositum.getChild(i).getText());
      } 

      ArticlePlusNomen subject = new ArticlePlusNomen(subjectArticle, subjectNomen);
      ArticlePlusNomen object = new ArticlePlusNomen(objectArticle, objectNomen);
      Sentence sen = new Sentence(subject, verb, object);


      System.out.println("" + subject.getList());
      System.out.println("" + object.getList());
      System.out.println("" + sen.getList());
    }
  }

}

Die Ausgabe lautet:

Der
Hamster
liebt
das
Rad
[Der, Hamster]
[das, Rad]
[Der, Hamster, liebt, das, Rad]
Der
Hund
begehrt
die
Wurst
[Der, Hund]
[die, Wurst]
[Der, Hund, begehrt, die, Wurst]
Der
Vater
liest
die
Zeitung
[Der, Vater]
[die, Zeitung]
[Der, Vater, liest, die, Zeitung] :autsch:

Es geht hier um die Umsetzung des Composite Patterns. Vorgabe war, dass man die Komponenten in den Composites über den Konstruktor aufruft und nicht direkt in den Methoden. Zudem sollen Interfaces anstatt abstrakte Klassen verwendet werden. Evtl. kann mir ja doch noch jemand die Frage beantworten, was da der Vorteil ist. Insgesamt wäre der Ansatz mit einer abstrakten Klasse und ohne Referenz über einen Konstruktor einfacher in der Umsetzung gewesen.
 

innoc

Aktives Mitglied
Hallo Crian,

der Programmcode in seiner jetzigen Form ist halt nur ein Startpunkt und nur auf Sätze der Form Artikel-Nomen-Verb-Artikel-Nomen ausgelegt und daher auf Sonderfälle wie Abkürzungen nicht anwendbar. Es gibt im Code übrigens noch einen gravierenderen Bug. Durch die LinkedHashSet-Liste werden auch doppelt vorkommende gleiche Artikel entfernt, also z.B. "die Maus liebt die Katze" wird dann zu "die Maus liebt Katze". Daher muss die Dubletten-Bereinigung in den for-Schleifen zu subject und object von Sentence.class durchgeführt werden.
 

Crian

Top Contributor
Ah gut, dann ist das mit den Abkürzungen erstmal nicht so wichtig. Um dein Problem zu beseitigen, könntest du statt einer LinkedHashSet-Liste vielleicht einfach eine ArrayList verwenden, falls du das LinkedHashSet nicht aus anderen Gründen benötigst.
 

Ähnliche Java Themen

Neue Themen


Oben