Änderungen innerhalb for-Schleife speichern

oetzi

Bekanntes Mitglied
Hallo zusammen,

Bei einer For-Schleife mit einem Schleifenkopf wie diesem hier:
for (int i = 0; i < liste.size(); i++)
greife ich ja ganz einfach über liste.get(i) auf ein element zu, kann es verändern und über liste.set(i) das Element in der Liste speichern/aktualisieren.

soweit so gut, aber wie sieht es bei dieser Form der for-Schleife aus: (Gibt es eigentlich Bezeichner für die beiden For-Schleifenarten?)
for (iterable_type iterable_element : iterable)
Hier ist der zugriff ja noch simpler einfach über iterable_element möglich.
Aber ich habe gerade das Problem, dass ich nicht drauf komme, wie ich das entsprechende Element in der Liste aktualisiere.

Ein Beispiel:
element.name sei auf "blub" gesetzt
Java:
for (iterable_type element : iterable){
  element.setName("bla");
  System.out.println(element.getName());
  //Ausgabe ist "bla" <- wie erwartet
}
for (iterable_type element : iterable){
  System.out.println(element.getName());
  //Ausgabe ist "blub" und nicht "bla" <- Fehler bzw. Problem
}

Wie stelle ich es an, dass das Element in der Liste gespeichert wird?

Gruß
oetzi
 
Zuletzt bearbeitet:
Hallo Marco,

die sind soweit korrekt. Habe mal das Beispiel erweitert um das deutlich zu machen.

Willst du mir denn damit sagen, dass es so schon funktionieren sollte wie es in dem Beispiel gezeigt ist?
 
iterable_type ?? Klassen schreibt man groß und CamelCase... was machst du da in der setName? Das müsste auf jeden Fall klappen...

die sind soweit korrekt.
bin ich mir nicht sicher...

Java:
public class Value {
   
   private String name;
   

   public String getName() {
      return name;
   }


   public void setName(String name) {
      this.name = name;
   }


   public static void main(String[] args) {
      
      Value value = new Value();
      value.setName("test");
      ArrayList<Value> liste = new ArrayList<Value>();
      liste.add(value);
      
      for (Value element : liste){
         element.setName("bla");
         System.out.println(element.getName());
         //Ausgabe ist "bla" <- wie erwartet
       }
       for (Value element : liste){
         System.out.println(element.getName());
       // -> bla wie erwartet...
       }
            
   }
}
klappt....
 
Folgendes
Java:
import java.util.ArrayList;
import java.util.List;

public class ForLoop {

    private static class Element {
        private String name;
        public Element(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    
    public static void main(String[] args) {
        List<Element> elements = new ArrayList<ForLoop.Element>();
        elements.add(new Element("blub"));
        for (Element element : elements){
            element.setName("bla");
            System.out.println(element.getName());
            //Ausgabe ist "bla" <- wie erwartet
          }
        for (Element element : elements){
            System.out.println(element.getName());
            //Ausgabe ist "blub" und nicht "bla" <- Fehler bzw. Problem
          }
    }
}

resultiert bei mir in der Ausgabe:
Code:
bla
bla

Vermutung: Deine Set Methode sieht so aus:

Java:
...
public void setName(String name) {
  name = name
}
...

und ist somit nicht korrekt!
 
da haten wir die gleiche idee..

Vermutung: Deine Set Methode sieht so aus:

public void setName(String name) {
name = name
}
...
und ist somit nicht korrekt!
mhn dachte ich mir zuerst auch, aber dann würde die erste ausgabe auch nicht stimmen... also auf
den source der das vom treadsteller gepostete ergebnis liefert bin ich gespannt....
 
Vielen Dank für eure Mühe!
Ich hatte ein vereinfachtes Beispiel oben verwendet, um euch weniger Arbeit zu machen die Problematik zu erkennen.

Leider befürchte ich, dass ich damit auch das eigentlich Problem versteckt habe...

Also hier jetzt der orginial Quellcode, der Probleme macht:
Zur Info: printMe() gibt ganz einfach sämtliche Attribute der Klasse aus (quasi debug Ausgabe)
Java:
		for (Monster m : monsterList) {
			for (Item i : m.getItemList()) {
				//recognize current worn status
				tmpWorn = i.isWorn();
				//get complete item
				tmpItem = is.getNewStandardItem(i.getPk_item());
				//set the recognized worn status
				tmpItem.setWorn(tmpWorn);
				i = tmpItem;	       //liegt hier das Problem?
				System.out.println("--- DANACH");
				i.printMe();			//<-- Diese Ausgabe gibt die Werte des Items so aus, wie sie sein sollten
			}
		}

//DEBUG OUTPUT
		for (Monster m : monsterList) {
				for (Item i : m.getItemList()) {
					System.out.println("--- GANZ DANACH");
					i.printMe();	 //<-- Hier wird leider nur ein "leeres" item ausgeben.
				}
		}

muss ich die Zeile 9 (i = tmpItem; ) irgendwie anpassen, damit das läuft? Oder liegt das Problem woanders?
 
du hast eine temp variable (tmpItem) und mit der hantierst du rum..,. das ändert aber nix an deinem eigentlichen Objekt.

das zuweisen reicht da nicht aus.... entweder du setzt dieses tmpItem dann in die liste (also ueberschreibst das bestehende objekt an der entsprechenden stelle) oder du hast eine art copy methode die die untersch. Werte von tmpItem direkt ins [c]i[/c] objekt setzt
 
oh man, über diese "Zuweisungs Problematik" stolper ich immer mal wieder...

das zuweisen reicht da nicht aus.... entweder du setzt dieses tmpItem dann in die liste (also ueberschreibst das bestehende objekt an der entsprechenden stelle) oder du hast eine art copy methode die die untersch. Werte von tmpItem direkt ins [c]i[/c] objekt setzt

Ja, wie macht man das denn jetzt am sinnvollsten?
Wenn ich das Element in der Liste mit dem tmpObjekt ersetzten könnte, wäre das wohl die einfachste Variante. Oder verbirgt sich dahinter wieder ein Fallstrick? 🙂
Wie würde ich denn dann dieses Element ersetzen? Eigentlich war ja genau das meine Eingangsfrage.


EDIT:
Denk dir nix dabei. Ist ein standard Anfängerfehler. Passiert den meisten am Anfang.
Danke, aber ich bin jetzt mal so ehrlich und behaupte, dass ich eigentlich gar kein Anfänger mehr bin ;-) Aber wie gesagt, über diese Problematik stolper ich irgendwie immer wieder. Kriege da keinen richtigen Überblick, wann und wo eine normale Zuweisung auf ein anderen Objekt reicht und wann ich es "kopieren" muss...
 
Zuletzt bearbeitet:
Ja, wie macht man das denn jetzt am sinnvollsten?
mhm ist natuerlich nicht so trivial zu beantworten.

ich würde mir mal die funktion [c]tmpItem = is.getNewStandardItem(i.getPk_item());[/c] anschauen. was passiert hier... warum gibts etwas von i rein und ein völlig anderes kommt raus ?

nochmals - die Ratschläge sind ohne komplette Systemkenntnis....

naiv schaut das ganze nach einem Widerspruch zu "Tell dont ask" aus.
Java:
for (Item i : m.getItemList()) {
                //recognize current worn status
                tmpWorn = i.isWorn();
                //get complete item
                tmpItem = is.getNewStandardItem(i.getPk_item());
                //set the recognized worn status
                tmpItem.setWorn(tmpWorn);
                i = tmpItem;           //liegt hier das Problem?
                System.out.println("--- DANACH");
                i.printMe();            //<-- Diese Ausgabe gibt die Werte des Items so aus, wie sie sein sollten
            }
alles läuft hier mit Item, ausser [c]is[/c]... warum ist das dann nicht Teil der Itemklasse ?
ala
Java:
for (Item i : m.getItemList()) {
      i.setWormFromXYZ(is); // was auch immer is ist
}

allgemein sollte man so gut wie möglich dem Tell dont ask prinzip folgen... obs immer durchsetzbar ist ist situationsabhängig.

brachiallösung ist eine neue Liste zu nehmen und die geänderten Items darein zu stecken und dann mit dieser neuen Liste zu arbeiten

oder

eine copy methode zu haben ala
Java:
i.changeItem(tmpItem)
... ansich aber ist die erste Variante vorzuziehen
 
Kriege da keinen richtigen Überblick, wann und wo eine normale Zuweisung auf ein anderen Objekt reicht und wann ich es "kopieren" muss...
bei einer zuweisung änderst du nicht die referenz sondern, die variable zeigt auf eine andere referenz.
A zeigt auf X
B = A
B zeigt auf X
B = Y
deswegen zeigt A noch immer auf X...


wenn mans niciht versteht, versucht man einfach ein paar beispiele, nimmt was an, schaut was passiert und lernt daraus...

Java:
public class Value {
   
   private String wert;
      
   public String getWert() {
      return wert;
   }
   public void setWert(String wert) {
      this.wert = wert;
   }
      
   public Value(String wert) {
      super();
      this.wert = wert;
   }
   
   public static void main(String[] args) throws IOException {
      
      Value wert1 = new Value("wert1");
      
      ArrayList<Value> list = new ArrayList<Value>();
      list.add(wert1); //list.get(0) zeigt jetzt auf das objekt mit wert1
      System.out.println(list.get(0).getWert());
      
      wert1 = new Value("wert2"); //wert1 zeigt jetzt auf das objekt mit wert2
      System.out.println(wert1.getWert());
      
      //wohing zeigt list.get(0) ? 1 oder 2?
      //natürlich 1, nur weil jetzt die variable wert1 wo anders hinzeigt, heißt das ja nicht das sich die referenz die in list.get(0) drinnen ist, anders ist
      System.out.println(list.get(0).getWert());
      
      //jetzt will ich aber den inhalt von wert1 ändern
      list.set(0, new Value("wert3")); //ok, list.get(0) ist jetzt wert3, aber das wert1 objekt hab ich nicht geändert, ich hab ein neues erstellt
      System.out.println(list.get(0).getWert());
      //da keiner mehr auf das wert1 objekt zeigt, wird es vom gc gelöscht...
      
      list.get(0).setWert("wert4"); //so wärs logisch da ich jetzt das konkrete objekt verändere
      System.out.println(list.get(0).getWert());
      
    }
}
 
ich würde mir mal die funktion [c]tmpItem = is.getNewStandardItem(i.getPk_item());[/c] anschauen. was passiert hier... warum gibts etwas von i rein und ein völlig anderes kommt raus ?
okay, kurz zur Aufklärung der Bezeichner:
pk_item = Id des Items
is = itemService; also eine Klasse die mir alle möglichen Sachen rund um mein Item anbietet.
Diese Klasse enthält unter anderem eine ArrayList allItemsList mit allen verfügbaren Items.

Die Situation vor dieser Codestelle ist, dass ich aus der Datenbank die Item-Ids geholt habe, die das Monster hat. Um jetzt nicht zig Mal aus der Datenbank mir die selben Daten (der Items) zu holen, nutze ich die Methode getNewStandardItem(Integer). Diese kriegt die Item-Id übergeben, sucht sich aus der allItemsList das entsprechende raus und gibt es zurück.

naiv schaut das ganze nach einem Widerspruch zu "Tell dont ask" aus.
mhh, ehrlich gesagt habe ich von diesem "Tell don't ask" Prinzip noch nicht gehört. Ich denke aber mal, dass das nicht zu befolgen ist, wenn man sich solcher Service Klassen bedient.

brachiallösung ist eine neue Liste zu nehmen und die geänderten Items darein zu stecken und dann mit dieser neuen Liste zu arbeiten

du meinst wahrscheinlich so oder?
Java:
		for (Monster m : monsterList) {
			ArrayList<Item> tmpList = new ArrayList<Item>()
			for (Item i : m.getItemList()) {
				//recognize current worn status
				tmpWorn = i.isWorn();
				//get complete item
				tmpItem = is.getNewStandardItem(i.getPk_item());
				//set the recognized worn status
				tmpItem.setWorn(tmpWorn);
				tmpList.add(tmpItem);		
			}
           m.setItemList(tmpList);
		}

Das halte ich jetzt gar nicht mal für eine so schlechte Lösung! 🙂
Aber du sprachest ja von "Brachiallösung". Gibt es irgendwas daran auszusetzen oder ein Problem bei dem Vorgehen?

eine copy methode zu haben ala
Java:
i.changeItem(tmpItem)
... ansich aber ist die erste Variante vorzuziehen

So eine Geschichte hatte ich hier im Forum vor gar nicht allzu langer Zeit schonmal diskutiert.
Da mein Item u.a. wieder andere Objekte enthält, müsste ich allen (beteiligten) Objekten solch eine "Copy-Methode" spendieren und all diese Methoden nacheinander aufrufen lassen.
Irgendwie hätte ich kein gutes Gefühl dabei, solch eine verzweigte Struktur zu erstellen. Dann notfalls lieber das ganze über eine tiefe Kopie lösen.


Aber unabhängig davon: Spricht was gegen die Lösung mit der tmpList?
 
wenn mans niciht versteht, versucht man einfach ein paar beispiele, nimmt was an, schaut was passiert und lernt daraus...

Jo, das tue ich durchaus! Das verhindert aber leider trotzdem nicht, dass man manchmal über Sachen stolpert, wo man meinte man hätte sie verstanden.

Nehmen wir mal das Beispiel von dir:

Java:
      wert1 = new Value("wert2"); //wert1 zeigt jetzt auf das objekt mit wert2
      System.out.println(wert1.getWert());

Hier ist es ja eignetlich völlig klar, dass durch die Zuweisung "der Zeiger" wert1 auf das neue Objekt wert2 zeigt.

Aber wo ist denn der Unterschied zu meinem Fall:
Java:
i = tmpItem;
Hier hätte ich nämlich genau das Gleiche angenommen, was du mir gezeigt hast. Nämlich, dass ich den Zeiger i auf die Speicheradresse von tmpItem umbiege.

Aber hier passiert das nicht, sonst hätte ich danach ja kein leeres Item.

Wo ist also hier der Unterschied zwischen diesen beiden Beispielen?
Ich hoffe jetzt ist klar, was ich mit "über bekannte Sachen stolpern" meinte 🙂
 
for (Item i : m.getItemList()) {

ist nix anderes als
for(int y = 0;y < m.getItemList().size(); y++){
also
Java:
for(int y = 0;y < m.getItemList().size(); y++){
 Item i = m.getItemList().get(y);
 ....
  i = tmpItem;
}

Nämlich, dass ich den Zeiger i auf die Speicheradresse von tmpItem umbiege
ja stimmt wird auch gemcht, nur der zeiger m.getItemList().get(2) zum beispiel, zeigt noch immer auf das alte objekt, der hat von der i = tmpItem; zuweisung nix mitbekommen ...
 
mhh, ehrlich gesagt habe ich von diesem "Tell don't ask" Prinzip noch nicht gehört. Ich denke aber mal, dass das nicht zu befolgen ist, wenn man sich solcher Service Klassen bedient.
das prinzip ist eins der wichtigen Elemente sauberer Programmierung - bsp Tell, Don't Ask!

es sollte immer befolgt werden - im Rahmen von Servicen ist es natürlich schwieriger umzusetzen. Serivce sollten aber nicht dafür existieren Objekte zu manipulieren oder anhand gewisser Eigenschaften ein Verhalten des Objekts zu triggern - das können oder sollen die Objekte selber tun. Service sollen eine Schicht drüber liegen (mir fällt der name grad net ein) - infrastrukturelle Fragestellungen beantworten (hol Objekte aus der DB).

Die Diskussion stammt aus dem sog. Domain-Driven-Design und würde in der Gesamtheit hier etwas viel

daher Rule of thumb: Wende Tell dont ask an ! ;-)
 
okay, ich habe gerade nochmal ein bisschen rumgetestet und weiß jetzt, wo genau der Haken bei der Sache ist.

Folgender Code:
Java:
public class ForSchleifenTest {

	   private String wert;
	      
	   public String getWert() {
	      return wert;
	   }
	   public void setWert(String wert) {
	      this.wert = wert;
	   }
	      
	   public ForSchleifenTest(String wert) {
	      super();
	      this.wert = wert;
	   }

	public static void main(String[] args) {
		ArrayList<ForSchleifenTest> liste = new ArrayList<ForSchleifenTest>();
		liste.add(new ForSchleifenTest("A"));
		liste.add(new ForSchleifenTest("B"));
		liste.add(new ForSchleifenTest("C"));		
		System.out.println("1:");
		for (ForSchleifenTest f : liste) {
			System.out.println(f.getWert());
		}
		
		for (ForSchleifenTest f : liste) {
			f.setWert("X");
		}
		System.out.println("2:");
		for (ForSchleifenTest f : liste) {
			System.out.println(f.getWert());
		}
		
		for (ForSchleifenTest f : liste) {
			f = new ForSchleifenTest("Y");
		}
		System.out.println("3:");
		for (ForSchleifenTest f : liste) {
			System.out.println(f.getWert());
		}
	}

}

Führt zu folgendem Ergebnis:
1:
A
B
C

2:
X
X
X

3:
X
X
X

Der Haken ist also, dass ich bei so einer For-Schleife:
for (ForSchleifenTest f : liste)
mit f.setXY() einen Wert verändern kann, aber nicht mit f = new Object() das eigentlich Objekt in der Liste ersetzen kann.
DAS war der Stolperstein heute Vormittag ;-)

okay, ich versuche es mal selber zu erklären:
1. f liefert mir den Zeiger auf das Objekt. mit f.setXY greife ich also auf den Speicherbereich das Objektes, wo mein XY drin steht und kann es somit verändern.
2. ahh und ich denke mal, dass f halt nur EIN Zeiger auf das Objekt ist, die ArrayList aber einen ANDEREN Zeiger auf das Objekt hat. Somit ändere ich den "lokalen" Zeiger auf das neue Objekt, aber nicht den der ArrayListe.
Richtig?? 🙂
 
das prinzip ist eins der wichtigen Elemente sauberer Programmierung - bsp Tell, Don't Ask!

es sollte immer befolgt werden - im Rahmen von Servicen ist es natürlich schwieriger umzusetzen. Serivce sollten aber nicht dafür existieren Objekte zu manipulieren oder anhand gewisser Eigenschaften ein Verhalten des Objekts zu triggern - das können oder sollen die Objekte selber tun. Service sollen eine Schicht drüber liegen (mir fällt der name grad net ein) - infrastrukturelle Fragestellungen beantworten (hol Objekte aus der DB).

Die Diskussion stammt aus dem sog. Domain-Driven-Design und würde in der Gesamtheit hier etwas viel

daher Rule of thumb: Wende Tell dont ask an ! ;-)

okay, jetzt mal rein basierend auf dem Text den du da verlinkt hast, kommt bei mir das ungute Gefühl auf, dass ich den Spaß mit den Services wohl nicht so umgesetzt habe, wie es eigentlich sein sollte... 🙁
Und dabei hatte ich vor gar nicht allzu langer Zeit die Services per umfangreichen Re-Engenieering erst eingebaut.

Naja, das ganze Projekt sehe ich als Übungsprojekt an um mich weiterzubilden.

Und weitergebildet habe ich mich dank eurer Hilfe heute definitiv! 🙂 Vielen Dank nochmal dafür!
 

Neue Themen


Zurück
Oben