Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
Hallo,
bevor ich mit der Aufgabe anfange, habe ich noch ein paar Verständnisfragen.
1. Was ist mit "StringSet" gemeint? Was ein String ist, weiß ich. "to set" bedeutet aufstellen,festlegen,legen, setzen,...
2. Was ist mit "Speichern Sie die Elemente einer Menge mit Kapazität n und Kardinalität m in den ersten m Plätzen eines Arrays der Größe n!" gemeint?
3. Warum ist in der remove Methode der Parameter vom Typ "StringS"? Müsste es nicht String sein?
Also StringSet ist ein Set of String. Also nicht to set sondern nur set. Und das ist das Wort für Menge (aus mathematischer Sicht). Also eine Menge von Strings.
Kardinalität einer Menge ist die Größe der Menge. Kapazität meint, die maximale Anzahl an Elementen, die gespeichert werden können. StringSet erzeugst Du ja mit der gewünschten Kapazität (n) und fügst dann Elemente ein (m).
Und das Strings ist ein Schreibfehler denke ich mal. Das sollte einfach String sein. (Typ Strings gibt es ja auch nicht.)
So sieht mein aktueller Code aus. Es sind einige Fehler drin. Das ist aber erstmal nicht das Problem.
Ich bin gerade bei der Methode contains und ich weiß nicht mit was, ich mein String vergleichen soll. Es müsste irgendein Objekt vom Typ StringSet sein.
Java:
public class StringSet {
private int capacity;
private int card;
public StringSet(int n){
capacity=n;
}
public StringSet(int n, String s){
this(n);
insert(s);
}
public int capacity(){
return capacity;
}
public int card(){
return card;
}
public void print(){
//....
}
public boolean contains(String s){
for(int i=0;i<capacity;i++){
if(s.equals())
}
return true;
}
public boolean insert(String s){
//....
return true;
}
boolean remove(String s){
//....
return true;
}
public static StringSet intersection(StringSet first, StringSet second){
}
}
Also Du musst Dir überlegen, wie Du Strings speichern willst in dem StringSet. Da die maximale Anzahl bekannt ist, kannst Du Dir überlegen, wie Du n Strings aufbewahren kannst.
Sobald Du das weißt, kannst Du auch die Funktionen mit Code füllen. Dann kannst Du etwas einfügen, prüfen, ob etwas enthalten ist, u.s.w.
Also ich würde ein String Array nehmen.
Nur, an welcher Stelle deklariere ich das String Array? Ich würde sagen, als private Objektvariable. Das Problem ist aber, mein n ist zu diesem Zeitpunkt unbekannt.
Dein Ansatz ist schon richtig. Das n bekommst Du doch im Konstruktor, d.h. Du kannst eine Instanz-Variable deklarieren welches Du erst im Konstruktor initialisierst.
public class StringSet {
private String[] strings;
private int capacity;
private int card;
public StringSet(int n){
capacity=n;
strings=new String[n];
}
public StringSet(int n, String s){
this(n);
insert(s);
}
So sieht meine aktuelle Lösung aus. Es fehlt nur noch, die Methode intersection.
1. Habt ihr irgendwelche Verbesserungsvorschläge? Was hätte ich besser machen können?
2. Habe ich irgendwo redundanten Code?
3. Warum ist die intersection Methode eine statische Methode? Woher weiß ich, dass diese statisch sein muss?
Java:
public class StringSet {
//Habe ich irgendwo redundanten Code?
private String[] strings;
private int capacity;
private int card;
public StringSet(int n){
capacity=n;
strings=new String[n];
}
public StringSet(int n, String s){
this(n);
insert(s);
}
public int capacity(){
return capacity;
}
public int card(){
return card;
}
public void print(){
System.out.print("{ ");
for(int i=0;i<card-1;i++){
System.out.print(strings[i]+", ");
}
System.out.print(strings[card-1]+" }");
}
public boolean contains(String s){
for(int i=0;i<capacity;i++){
if(s.equals(strings[i])) return true;
}
return false;
}
public boolean insert(String s){
if(!contains(s) && s!=null && card<capacity ){ //s!=null als erste Bedingung?
for(int i=0;i<capacity;i++){
if(strings[i]==null){
strings[i]=s;
break;
}
}
return true;
}
return false;
}
boolean remove(String s){
if(contains(s) && s!=null){ //s!=null als erste Bedingung?
for(int i=0;i<card;i++){
if(strings[i].equals(s)){
strings[i]=null;
return true;
}
}
}
return false;
}
public static StringSet intersection(StringSet first, StringSet second){
}
}
1. Hier mal einen Code, der ein wenig aufgeräumter ist und Prüfungen vor den Aktionen durchführt.
2. Versteckt: ja
3. Designentscheidung - ist aber eine logische Folgerung, da die Methode nur von den Parameter abhängt und ein neues StringSet zurückliefert.
Java:
class StringSet {
private final String[] strings;
private final int capacity;
private int card;
public StringSet(int n) {
if (n < 0) {
throw new IllegalArgumentException("n must be >= 0");
}
capacity = n;
strings = new String[n];
card = 0;
}
public StringSet(int n, String s) {
this(n);
insert(s);
}
public int capacity() {
return capacity;
}
public int card() {
return card;
}
public void print() {
System.out.print("{ ");
for (int i = 0; i < card; i++) {
if (i != 0) {
System.out.print(", ");
}
System.out.print(strings[i]);
}
System.out.print(" }");
}
public boolean contains(String s) {
if (s == null) {
return false;
}
return indexOf(s) != -1;
}
public boolean insert(String s) {
if (card >= capacity || s == null || contains(s)) {
return false;
}
strings[card++] = s;
return true;
}
boolean remove(String s) {
if (s == null) {
return false;
}
int index = indexOf(s);
if (index == -1) {
return false;
}
for (int i = index + 1; i < card; i++) {
strings[i - 1] = strings[i];
}
card--;
return true;
}
public static StringSet intersection(StringSet first, StringSet second) {
StringSet result = new StringSet(first.capacity() + second.capacity());
for (int i = 0; i < first.card; i++) {
String s = first.strings[i];
if (second.contains(s)) {
result.insert(s);
}
}
return result;
}
private int indexOf(String s) {
for (int i = 0; i < card; i++) {
if (s.equals(strings[i])) {
return i;
}
}
return -1;
}
}
Also ich habe mir Deinen Code einmal angesehen und habe ein paar Ideen / Verbesserungsvorschläge eingearbeitet.
a) getter fangen normalerweise immer mit get an, also getCard() und so.
b) Statt einer print funktion ist es üblich, toString zu überschreiben. Das @Override ist nicht notwendig und lass dich davon nicht irritieren. Das ist eine Annotation, die dem Compiler sagt, dass die Funktion eine andere Funktion überschreiben soll. Javatechnisch macht diese Annotation nichts. Nur der Compiler prüft dann, ob in der Super-Klasse wirklich so eine Funktion ist und wenn nicht, wird ein Fehler ausgegeben. Verhindert Schreibfehler und so.
c) Deine Logik ist nicht einheitlich. Bei insert und remove gehst Du davon aus, dass Du einfach irgendwo eine Lücke findest. Bei der Ausgabe gibst Du aber einfach die ersten Zeichen aus. Hier müsstest Du noch nachbessern. Was könntest du beim remove machen, damit keine Lücken entstehen? Was würde dies für das insert und contains bedeuten? Oder willst Du lieber das print / toString anpassen?
d) Variablennamen: Hier solltest Du immer saubere Namen verwenden. Code wird direkt lesbarer, wenn die Variablen direkt aussagen, was in ihnen steckt. strings ist gut, aber i, n, s? Nicht zögern, da auch Namen wie stringToSearch zu verwenden.
e) Die Prüfungen verstecke ich nicht alle zusammen in einem if. Bei uns im team ist es üblich, die Parameter und andere Vorbedingungen direkt am Anfang zu prüfen. Das habe ich einmal bei der insert Methode gezeigt. On Exceptions geworfen werden sollen oder ob hier ein false zurück gegeben werden sollte, ist Definitionssache.
f) Meine IDE stellt card grau da. Wird irgendwie nie richtig genutzt. Hast Du da evtl. etwas vergessen? Wann müsste card denn angepasst werden? (In meinem Code zu cardinal umbenannt. card düfte dafür ja eine Abkürzung gewesen sein. Oder müsste es sogar cardinalNumber heissen?
g) static können / sollten alle Funktionen sein, die keinen Zugriff auf die Instanz brauchen. Ist eine Designentscheidung, denn statt einem Aufruf mit zwei Parametern könnte man den ersten Parameter weglassen - das wäre dann die aktuelle Instanz. In dem Fall würde die Funktion nicht statisch sein.
Code:
public class StringSet {
private String[] strings;
private int capacity;
private int cardinal;
public StringSet(int capacity){
this.capacity = capacity;
strings = new String[capacity];
}
public StringSet(int capacity, String firstElement){
this(capacity);
insert(firstElement);
}
public int getCapacity(){
return capacity;
}
public int getCardinal(){
return cardinal;
}
public void print(){
System.out.print("{ ");
for(int index = 0; index< cardinal -1; index++){
System.out.print(strings[index]+", ");
}
System.out.print(strings[cardinal -1]+" }");
}
@Override
public String toString(){
StringBuilder result = new StringBuilder();
result.append("{ ");
for(int index = 0; index< cardinal -1; index++){
result.append(strings[index]);
result.append(", ");
}
result.append(strings[cardinal -1]);
result.append(" }");
return result.toString();
}
public boolean contains(String stringToSearch){
for(int i=0; i<capacity; i++){
if(stringToSearch.equals(strings[i])) return true;
}
return false;
}
public boolean insert(String newElement){
// First check arguments and other important requirements
if (newElement == null)
throw new IllegalArgumentException();
if (cardinal == capacity)
throw new IndexOutOfBoundsException();
if(!contains(newElement)){
for(int index=0; index<capacity; index++){
if(strings[index]==null){
strings[index]=newElement;
break;
}
}
return true;
}
return false;
}
boolean remove(String element){
if(contains(element) && element!=null){ //s!=null als erste Bedingung?
for(int i = 0; i< cardinal; i++){
if(strings[i].equals(element)){
strings[i]=null;
return true;
}
}
}
return false;
}
public static StringSet intersection(StringSet first, StringSet second){
return null;
}
}
@Flown: Die einzelnen Fehler hätte ich Ihn herausmachen lassen.
Und beim remove musst Du nicht das Array durchgehen. Du kannst einfach das letzte Element in die Lücke schreiben. Sets haben keine Reihenfolge, daher muss die Reihenfolge auch nicht beibehalten werden.
@kneitzel Das ist eine Implementierung von einer Liste die ich im Studium mal gemacht habe (ist schon ca. 5 Jahre her - also kein intelligentes remove). Habs nur umgeschrieben, darum keinen Aufwand betrieben (außer intersection und kleine Prüfungen).
Einmal einen sauberen Code sehen hilft oft neue Gedankengänge zu eröffnen und eigene Fehler aufzuzeigen. Wie man mit sowas dann selbst umgeht ist jedem selbst überlassen.
@kneitzel
Ich habe jetzt mal meinen Code bearbeitet. Die Musterlösung ist im Anhang. Ich hab mir die Lösung noch nicht angeschaut.
zu b) In der Aufgabenstellung steht, dass keine Java- Bibliotheksmethoden verwendet werden dürfen (bis auf equals, System.out.print, System.out.println). Aber du hast natürlich Recht.
zu c) Ich weiß nicht, ob ich das richtig verstanden habe, aber wenn es bei der insert/remove Methode keine Lücke gibt, dann wird doch das letzte Element entfernt.
Oder könntest du mit anderen Worten nochmal erklären, was das Problem ist?
zu f) Ich habe vergessen card in der insert/remove Methode zu vergrößern/verringern.
zu g) Und woher weiß/erkenne ich, dass die intersection Methode keinen Zugriff auf die Instanz braucht?
Java:
public class StringSet {
//Habe ich irgendwo redundanten Code?
private String[] strings;
private int capacity;
private int card;
public StringSet(int anzahlElemente){
capacity=anzahlElemente;
strings=new String[anzahlElemente];
}
public StringSet(int anzahlElemente, String teilmenge){
this(anzahlElemente);
insert(teilmenge);
}
public int getCapacity(){
return capacity;
}
public int getCard(){
return card;
}
public void print(){
System.out.print("{ ");
for(int element=0;element<card-1;element++){
System.out.print(strings[element]+", ");
}
System.out.print(strings[card-1]+" }");
}
public boolean contains(String teilmenge){
for(int element=0;element<capacity;element++){
if(teilmenge.equals(strings[element])) return true;
}
return false;
}
public boolean insert(String teilenge){
if(teilenge!=null &&!contains(teilenge) && card<capacity ){ //s!=null als erste Bedingung
for(int element=0;element<capacity;element++){
if(strings[element]==null){
strings[element]=teilenge;
card++;
break;
}
}
return true;
}
return false;
}
boolean remove(String teilmenge){
if(teilmenge!=null && contains(teilmenge) ){ //s!=null als erste Bedingung
for(int element=0;element<card;element++){
if(strings[element].equals(teilmenge)){
strings[element]=null;
return true;
}
}
}
return false;
}
public static StringSet intersection(StringSet first, StringSet second){
return null;
}
}
An dem was die Methode tut. Im vorliegenden Fall ist es eigentlich Unsinn die static zu machen weil du auf jeden Fall zwei Instanzen der Klasse StringSet brauchst.
Also Du hast ein Array einer bestimmten Größe und Du hast eine Variable, die die Anzahl der Elemente beinhaltet.
Da es hier um ein Set geht, ist die Reihenfolge egal. Mengenlehre - Eine Menge hat nur einen Inhalt aber keine Reihenfolge.
Also macht es doch Sinn, dass Du alle Felder von Anfang an schön füllst. Wenn Du ein Element hinzu fügst und du hast schon x Elemente, dann ist klar: Das neue Element kommt an Platz x (weil die Plätze 0 - x-1 bereits voll sind).
Wenn Du ein Element heraus nimmst, dann entsteht eine Lücke. Da wir aber die Reihenfolge nicht beachten müssen, können wir in die Lücke einfach das letzte Element schreiben.
Und Du schreibst die Funktion einfach. Und wenn Du an keiner Stelle "this" benötigst, dann kannst Du die Funktion static machen. Und dies hat einige Vorteile, weshalb es eine Regel sein sollte, Funktionen, die kein "this" benutzen, immer statisch zu machen. (Es gibt ganz wenige Ausnahmen. Static verhält sich halt etwas anders was die Möglichkeiten angeht, die man damit hat.)
@JStein52: Wieso ist das Quatsch? Das static ist hier eine gute Sache, denn es wird kein this benötigt. Ob das Design gut ist, ist eine andere Frage. Ich selbst würde statt
StringSet.intersect(set1,set2) auch ein set1.intersect(set2) besser finden. Aber wenn da zwei Parameter vorgegeben sind, dann ist die Funktion auch statisch zu machen.
Dann schon !! War das denn so ? Das konnte ich nirgends erkennen. Und ausserdem wollte der TE wissen an was er das erkennt. Und im vorliegenden Fall erkennt er es eben an gar nichts dass hier static stehen muss/soll
Das habe ich übrigens noch nicht verstanden. Wenn das das Entscheidungskriterium wäre dann könnte man sehr vieles static machen indem man einfach die beteiligten Objekte als Parameter übergibt.
Also Du musst die Logik richtig verstehen. Kleiner Vergleich am Anfang;
Ich gebe dir 10 Euro => Du kannst Dir ein EIs kaufen.
Du kannst dir ein Eis kaufen => Ich muss Dir nicht unbedingt 10 Euro gegeben haben!
Also die Logik besagt nur, dass Du, wenn Du eine Funktion geschrieben hast, die nicht auf eine Instanz zugreift, diese Funktion static machen solltest.
Es ist nicht gesagt, dass es erstrebenswert ist, statische Funktionen zu haben. (Eher das Gegenteil!)
So gibt es im Visual Studio (ab einer Edition - ich meine Pro) eine statische Code Review. Und da ist eine Regel, dass eine Funktion, die nicht auf die Instanz zugreift, statisch sein sollte. (Klar, es gibt zwischen Java und c#/.Net einige Unterschiede, aber ich habe das Verhalten recherchiert und diese Regel trifft auch auf Java zu. )
Und bei C# geht es sogar noch etwas weiter. Da diese ganzen Aufrufe von statischen Funktionen absolut mangelhaft sind (aus meiner Sicht) bietet c# eine Möglichkeit, fremde Klassen zu "erweitern". Beispiel wäre eine Klasse StringUtil. In der schreibe ich eine Funktion: isEmptyOrNull. Wenn ich dies nun nutzen will, dann schreibe ich überall StringUtil.isEmptyOrNull(myString). Schöner wäre aber ein Aufruf myString.isEmptyOrNull() und das erlaubt C#. Der erste Parameter der static Funktion bekommt noch das Schlüsselwort this und schon erlaubt C# die zweite Schreibweise.
Ok, das letzte Beispiel ist nun für Java uninteressant, aber der objektorientierte Ansatz ist in der Denkweise ja, dass Objekte sich gegenseitig Nachrichten schicken. Es gibt (gab?) da einige interessanten Bücher aber ich weiss nicht, ob so heute noch objektorientierte Entwicklung erläutert wird
@kneitzel
was ist aber, wenn ich das letzte Element entferne?
Dann entsteht keine Lücke.
Also sollte ich zuerst überprüfen ob nicht das letzte Element gelöscht werden soll und dann das letzte Element in die Lücke einfügen.
Also es ist immer wichtig, dass man sich die Spezialfälle überlegt um genau zu prüfen, ob hier eine Sonderbehandlung notwendig ist:
card ist 1 (nur ein Element).
Dann durchsuchst Du das Array nach dem Element und durchsuchst die Elemente 0 bis 1-1 = 0.
Das Element 0 ist nun ein Treffer.
Nun kopierst Du das letzte Element (card-1 = 0) in das aktuelle Element(0) - das bringt keine Probleme mit sich.
Nun setzt Du evtl. noch das letzte Element auf null (Damit ist dann keine Referenz mehr da und ggf. wird das Element vom GC eingesammelt) - das ist ein Punkt, der bisher wohl nicht behandelt wurde in den Lösungen hier.
Nun reduzierst Du card um 1 auf 0.
Sieht soweit ganz gut aus. Ebenso ist es ein Spezialfall, wenn das letzte Element der Treffer ist. Aber da passiert genau das Gleiche, so dass es auch hier kein Problem gibt.
Das meinte ich ja. Der bessere Ansatz wäre hier in dem Fall für intersection(...) ein Parameter und nicht static gewesen. Aber da es nun mal so vorgegeben war ist alles gut.
Die Regel ist aber dennoch interessant, denn manchmal hat man so kleine Funktionen, die eben kein this benötigen. Ein Beispiel war jetzt erst beim Lottozahlen-Thread die Ermittlung einer Zufallszahl in einem Bereich von - bis. Da war dann kein "this" notwendig und damit konnte die Funktion auch static sein. Ist eigentlich eine Kleinigkeit, aber so hätte die Funktion auch von anderen Klassen hätte aufgerufen werden können
Wobei dann auch ein Refactoring in eine andere Klasse wichtig wäre, denn (nur als fiktives Beispiel) wieso sollte ein Gewinnspiel unter Kunden eine Lotto-Klasse nutzen?
Die Regel bringt einen dann aber durch die Warnung dazu, das eigene Design zu prüfen. Dadurch dass die Funktion eben keine Instanz der Klasse benötigt, ist die Frage, in wie weit die Funktion wirklich zu der Klasse gehört, in dem sie derzeit ist. Geht halt alles in Richtung Clean Code.
Ich habe versucht, dass umzusetzen, was du mir gesagt hast.
Für den Quelltext der intersection Methode möchte ich mich im Voraus entschuldigen. Für die Variablen i, j, und k habe ich keine besseren Namen gefunden.
Java:
public class StringSet {
//Habe ich irgendwo redundanten Code?
private String[] strings;
private int capacity;
private int card;
public StringSet(int anzahlElemente){
capacity=anzahlElemente;
strings=new String[anzahlElemente];
}
public StringSet(int anzahlElemente, String teilmenge){
this(anzahlElemente);
insert(teilmenge);
}
public int getCapacity(){
return capacity;
}
public int getCard(){
return card;
}
public void print(){
System.out.print("{ ");
for(int element=0;element<card-1;element++){
System.out.print(strings[element]+", ");
}
System.out.print(strings[card-1]+" }");
}
public boolean contains(String teilmenge){
for(int element=0;element<capacity;element++){
if(teilmenge.equals(strings[element])) return true;
}
return false;
}
public boolean insert(String teilmenge){
if(teilmenge!=null &&!contains(teilmenge) && card<capacity ){ //s!=null als erste Bedingung
strings[card]=teilmenge;
card++;
return true;
}
return false;
}
boolean remove(String teilmenge){
if(teilmenge!=null && contains(teilmenge) ){ //s!=null als erste Bedingung
for(int element=0;element<card;element++){
if(strings[element].equals(teilmenge)){
strings[element]=strings[card-1];
card--;
return true;
}
}
}
return false;
}
public static StringSet intersection(StringSet first, StringSet second){
StringSet StringSetMin=(first.getCard()<=second.getCard())?first:second;
int min=(first.getCard()<=second.getCard())?first.getCard():second.getCard();
int max=(first.getCard()>second.getCard())?first.getCard():second.getCard();
StringSet schnittmenge=new StringSet(min);
int indexSchnittmenge=0;
for(int indexFirst=0;indexFirst<StringSetMin.getCard();indexFirst++){
for(int indexSecond=0;indexSecond<max;indexSecond++){
if(StringSetMin.strings[indexFirst].equals(StringSetMin.strings[indexSecond])){
schnittmenge.strings[indexSchnittmenge]=StringSetMin.strings[indexFirst];
indexSchnittmenge++;
}
}
}
return schnittmenge;
}
}
Also als erstes ist mir wichtig, dass Du Dich nicht entschuldigen musst. Wenn Du alles perfekt könntest, dann wärst Du wohl nicht hier.
Die Aufgabe ist erst einmal in mehrere Teilbereiche aufteilbar. Die erste Teilaufgabe ist die Frage, was die "geeignete Größe" ist für die Zielmenge. Und das hat Du aus meiner Sicht schon sehr gut gelöst, indem Du die kleinste Anzahl von beiden Mengen genommen hast.
Nur die aktuelle Lösung gefällt mir so noch nicht. Evtl. könntest Du mir einfach einmal in einfachen Worten beschreiben, wie Du vorgehen würdest, wenn Du das von Hand machen müsstest. Versuch dabei auf einem hohen Level zu bleiben. Also wenn ich es mit einem morgendlichen Ritual vergleichen müsste, dann wäre es mehr ein
- Ich ziehe mich an
- Ich frühstücke
- Ich verabschiede mich von meiner Frau
- Ich fahre auf Arbeit
Also ich will jetzt nicht wissen was für genaue Schleifen und Set-Zugriffe du machst. Sondern eher oberflächlich und ohne genaue Implementation.
Das ist aus meiner Sicht wichtig, denn so bekommst Du evtl. eine bessere Unterteilung. Man muss nicht immer alles in einer Funktion machen, sondern man kann Aufgaben unterteilen. Hat den Vorteil, dass man dann leicht Code "wiederverwenden" kann, d.h. Du nutzt die Funktion an mehreren Stellen Deiner Klasse, weil da oft Dinge benötigt werden, die öfters gebraucht werden.
- Ich zähle Menge 1 und 2 und wähle die kleinere Menge von beiden.
- Dann nehme ich das erste Element der kleineren Menge und vergleiche es mit allen Elementen der anderen Menge. Falls die Elemente übereinstimmen, dann kommt das in meine neue Menge rein.
Ja, das ist schon recht gut. Ich würde das "vergleiche es mit allen Elementen der anderen Menge" anders formulieren: Prüfen, ob das Element im anderen Set ist. Und wenn es im anderen Set ist, dann fügst Du es in das Zielset rein.
Und du musst nicht die kleinere Menge durchgehen. Du prüfst eigentlich immer gleich viel:
ob du nun n mal m Elemente durchsuchst oder m mal n Elemente kommt eigentlich auf das Gleiche raus.
Du musst die Größe also nur prüfen um min zu ermitteln für die Größe des Ziel-Sets.
Kannst Du mit diesen Informationen die Funktion umschreiben? Und Funktionen, die Du schon hast kannst Du dabei ja direkt nutzen!
Dann gehen wir einmal die Grundlagen durch:
Richtig erkannt hast Du:
- die Erzeugung der StringSet Instanz mit Größe min
- Du willst richtig alle Element aus first durchgehen.
Aber die Aufrufe sind dann jetzt falsch. Und du nutzt dann auch beim Einfügen nicht die bereits vorhandenen Funktionen.
Um zu prüfen, ob ein String in second ist, rufst Du doch second.contains(string) auf. Und string ist dann doch jetzt first.strings[index]. Somit kannst Du dann doch jetzt aus den beiden Informationen die Bedingung des if Befehls hin bekommen, oder?
Und dann willst Du etwas einfügen. Das hast Du schon fast richtig - nur eben fügst Du immer das Element 0 ein. Da musst du natürlich auch index nehmen: first.string[index] willst Du einfügen.
Aber Du hast jetzt natürlich den Code zum EInfügen doppelt. Zum einen in intersection ist dieser Code jetzt als auch in der Funktion insert. Daher solltest Du einfach second.insert(string) verwenden. string ist natürlich wieder second.strings[index].
Hat das zum Verständnis geholfen? Ansonsten bringe ich Dir als nächstes auch direkt eine intersection Lösung.
Also Du hast da immer noch die Dreher drin. Du gehst ja die Elemente von first durch, also musst Du in second prüfen, ob das Element da drin ist. Also immer second.contains(). Und Du willst ja das Element in schnittmenge einfügen, also schnittmenge.insert!
Also, stimmt auch noch der restliche Code?
In der Schnittmengen Methode habe ich, mit deiner Hilfe, die bereits fertig implementierten Methoden benutzt. Wie sieht es an anderer Stelle aus? Habe ich irgendwo redundanten Code?
Java:
public class StringSet {
//Habe ich irgendwo redundanten Code?
private String[] strings;
private int capacity;
private int card;
public StringSet(int anzahlElemente){
capacity=anzahlElemente;
strings=new String[anzahlElemente];
}
public StringSet(int anzahlElemente, String teilmenge){
this(anzahlElemente);
insert(teilmenge);
}
public int getCapacity(){
return capacity;
}
public int getCard(){
return card;
}
public void print(){
System.out.print("{ ");
for(int element=0;element<card-1;element++){
System.out.print(strings[element]+", ");
}
System.out.print(strings[card-1]+" }");
}
public boolean contains(String teilmenge){
for(int element=0;element<capacity;element++){
if(teilmenge.equals(strings[element])) return true;
}
return false;
}
public boolean insert(String teilmenge){
if(teilmenge!=null &&!contains(teilmenge) && card<capacity ){ //s!=null als erste Bedingung
strings[card]=teilmenge;
card++;
return true;
}
return false;
}
boolean remove(String teilmenge){
if(teilmenge!=null && contains(teilmenge) ){ //s!=null als erste Bedingung
for(int element=0;element<card;element++){
if(strings[element].equals(teilmenge)){
strings[element]=strings[card-1];
card--;
return true;
}
}
}
return false;
}
public static StringSet intersection(StringSet first, StringSet second){
int min=(first.getCard()<=second.getCard())?first.getCard():second.getCard();
StringSet schnittmenge=new StringSet(min);
for(int index=0;index<first.getCard();index++){
if(second.contains(first.strings[index]) ){
schnittmenge.insert(first.strings[index]);
}
}
return schnittmenge;
}
}
Also du hast recht oft teilmenge als Argument. Aber ist ein einzelnes Element eine Teilmenge? Teilmenge müssten ja auch mehrere Elemente sein können und das ist nicht der Fall.
Beim remove würde ich noch zwischen Zuweisung und card-- ein "strings[card-1]=null;" einbauen. Ansonsten halten wir noch eine Referenz und das wollen wir nicht.
Beim Testen des Programms kommt bei mir, wegen der print Funktion eine OutOfBounds Exception.
Ich habe das Programm mit der Windows Konsole ausgeführt. Deswegen kann ich nicht debuggen?
Starte es doch aus der IDE. Dann hast Du doch den Debugger. Im Augenblick sehe ich kein Problem - zeig doch mal den ganzen Code der Applikation, den du testest und gib uns die ganze Fehlermeldung / Stacktrace.
Wenn ich es aus der IDE starte, dann steht da "Console not available".
Hier der ganze Quelltext:
Java:
// Testprogramm für die Klasse StringSet.
class StringSetTest {
public static void main (String [] args) {
// Abbruch, wenn keine Konsole verfügbar ist (z. B. in Eclipse).
if (System.console() == null) {
System.out.println("console not available");
return;
}
// Aktuelle Menge s.
StringSet s = null;
// Endlosschleife.
while (true) {
// Kommandozeile lesen und in Wörter zerlegen.
// Abbruch bei Ende der Eingabe oder leerer Eingabezeile.
String line = System.console().readLine("command: ");
if (line == null || line.equals("")) return;
String [] cmd = line.split(" ");
// Fallunterscheidung anhand des ersten Zeichens des ersten Worts.
switch (cmd[0].charAt(0)) {
default: // new set
int n = Integer.parseInt(cmd[0]);
if (cmd.length == 1) s = new StringSet(n);
else s = new StringSet(n, cmd[1]);
break;
case '+': // insert
System.out.println(s.insert(cmd[1]));
break;
case '-': // remove
System.out.println(s.remove(cmd[1]));
break;
case '?': // contains
System.out.println(s.contains(cmd[1]));
break;
case '&': // intersection
StringSet t = new StringSet(cmd.length - 1);
for (int i = 1; i < cmd.length; i++) t.insert(cmd[i]);
s = StringSet.intersection(s, t);
break;
}
// Kardinalität, Kapazität und Inhalt der Menge s ausgeben.
System.out.print(s.getCard() + " of " + s.getCapacity() + " element(s): ");
s.print();
System.out.println();
}
}
}
Java:
public class StringSet {
//Habe ich irgendwo redundanten Code?
private String[] strings;
private int capacity;
private int card;
public StringSet(int anzahlElemente){
capacity=anzahlElemente;
strings=new String[anzahlElemente];
card=0; //Wichtig!!
}
public StringSet(int anzahlElemente, String string){
this(anzahlElemente);
insert(string);
}
public int getCapacity(){
return capacity;
}
public int getCard(){
return card;
}
// Position von s im Array elems liefern, falls es enthalten ist.
// Andernfalls -1 liefern.
//.............
public void print(){
System.out.print("{ ");
for(int element=0;element<card-1;element++){
System.out.print(strings[element]+", ");
}
System.out.print(strings[card-1]+" }");
}
public boolean contains(String string){
for(int element=0;element<capacity;element++){
if(string.equals(strings[element])) return true;
}
return false;
}
public boolean insert(String string){
if(string!=null &&!contains(string) && card<capacity ){ //s!=null als erste Bedingung
strings[card]=string;
card++;
return true;
}
return false;
}
boolean remove(String string){
if(string!=null && contains(string) ){ //s!=null als erste Bedingung
for(int element=0;element<card;element++){
if(strings[element].equals(string)){
strings[element]=strings[card-1];
strings[card-1]=null;
card--;
return true;
}
}
}
return false;
}
public static StringSet intersection(StringSet first, StringSet second){
int min=(first.getCard()<=second.getCard())?first.getCard():second.getCard();
StringSet schnittmenge=new StringSet(min);
for(int index=0;index<first.getCard();index++){
if(second.contains(first.strings[index]) ){
schnittmenge.insert(first.strings[index]);
}
}
return schnittmenge;
}
}
Ach ja - Du musst Dir Deine print Funktion einmal anschauen - was passiert bei einem leeren StringSet?
Kleine Anmerkung: Namen von Variablen sollten nicht den Typ sondern den Inhalt widerspiegeln. "string" ist ja ein Typ. Aber Du übergibst ja ein neues Element, ein zu suchendes Element, ein zu löschendes Element, ...
Da ist die Kardinalität 0. Also wird in strings[-1] etwas reingeschrieben, was zu einer Exception führt.
Soll ich zuerst abfragen, ob card>0 ist, dann wäre aber die leere Menge nicht mehr dabei.
Also Du solltest das auf jeden Fall abfragen und den aktuellen Code nur ausführen, wenn card > 0. Du kannst dann ja noch einen else Zweig erstellen, in dem Du diesen Spezialfall behandelst.