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.
hab eine eigene Klasse Vector2D mit der Methode changeDir(), die den Vektor einfach nur umdreht.
im Programm hab ich einen Vektor, den ich an vielen Stellen brauch und der daher nicht verändert werden soll.
für eine Berechnung brauch ich diesen Vektor allerdings umgedreht. Bisher hatte ich das so:
Vector2D vectorToChange=vectorConst;
vectorToChange.changeDir();
... Berechnungen mit vectorToChange
das Ergebnis beim ersten Durchlauf war dann immer ganz toll und beim zweiten Durchlauf der gleichen Methoden kam immer totaler Schrott raus.
schließlich hab ich dann also rausgefunden, dass vectorToChange im Prinzip nur der pointer auf den vectorConst ist (richtig?) und dieser daher mitgedreht wird (blöd...).
wie kopiere ich den vectorConst jetzt richtig, so dass er nicht mitverändert wird.
ich habs jetzt mal so gemacht, dass ich x1 x2 und so rauszieh und damit einen neuen Vector erstell, aber das ist halt total umständlich
public Vector2D(Vector2D v){
this.x=v.x;
this.y=v.y;
}
2) [kA ob's so geht] clone überschreiben:
Code:
@Override
public Vector2D clone(){
return new Vector2D(this.x,this.y);
}
3) [erfahrungsgemäß am praktischsten]
Mach deine Vector2D klasse vollständig immutabel, d.h. ist das objekt einmal erstelt, soll es niemals den Zustand verändern. Solche methoden wie changeDirection() schmeißt du dann raus. Stattdessen schreibst du dir sowas:
Code:
public Vector2D multiply(double d){
return new Vector2D(this.x*d,this.y*d);
}
und statt wie bsiher an irgendwelchen vektoren rumzudrehen und die dann zurückzudrehen, sagst du einfach:
Code:
Vector2D a=new vector2D(23,45);
Vector2D b=a.multiply(-1); //b ist jetzt ein völlig neuer umgedrehter a-vektor, hat auf a keinen einfluss.
Es ist irgendwie einfacher zu handhaben, sonst geht man irgendwann im chaos unter, wenn man dauern dran denken muss, was man alles kaputtgemacht hat, und in den ursprünglichen zustand zurückversetzen muss.
was erwartest du? wenn man einen Vector aus zwei doubles zusammenbaut, dann ist das immer so,
bei Gelegenheit A wie auch bei Gelegenheit B, C, D, E und F,
egal ob es mal umständlich erscheint oder mal ok ist,
die einzig andere Alternative wäre, durch allgemeine Mechanismen eine Kopie zu erstellen,
möchtest du das?
dein copy constructor war im prinzip meine lösung jetzt, da funktioniert das natürlich noch relativ einfach, aber bei größeren klassen mit mehr eigenschaften wird das doch schon ganz schön nervig oder?
dein zweiter lösungsvorschlag ist wohl wirklich am besten, bedeutet jetzt wohl nur ein bisschen arbeit.
Nein. 3) ist am besten, gefolgt von 1) und 2), wobei wie gesagt: kA wie sich das clone genau benimmt, das ist irgendwie nicht sonderlich typsicher, da besteht dein code irgendwann zur hälfte auf casting-anweisungen.
Mach's so wie in 3 beschrieben, ist wirklich am praktischsten, und man muss einfach nirgends irgendwas hinundherkopieren, weil es alles schon sofort bei den berechnungen gemacht wird.
Für "clone()" sollte (muss?) allerdings das Interface "Cloneable" implementiert werden. Die Dritte Lösung von 0x7F800000 verwende ich persönlich in etwas abgewandelter Form auch. Abgewandelt dahingehend, das ich den Ziel-Vector mitübergebe. Das hat den Vorteil, das nicht ständig ein neuer Vector zurückgegeben wird, der letztendlich nur den GabageCollector belastet.
public Vector2D multiply(Vector2D target, double d)
{
if(target == null) target = new Vector2D();
target.x = this.x * d;
target.y = this.y * d;
return target;
}[/code]... und so hat man stets die wahl...
Was heißt denn "GC belasten"? Wenn man das ergebnis nicht haben will, wieso soll man denn dann die methode aufrufen? Und wenn man das ergebnis haben will... nun ja... dann ist es halt da, da kommt man leider nicht drumherum den GC nicht zu belasten ???:L
Was soll ich dazu sagen... "Erfahrener Benutzer" steht (stand... wo isses hin?) diesmal nicht umsonst da... Ich hatte da mal so ein Problem mit Vector3D in JOGL. da ging es um Morph-Objekte. Das bedeutet, das ständig neue Vectoren erzeugt wurden. ...Irgendwann waren es zuviele... und das obige war die Lösung... Ich habe schlicht nicht mehr aktuelle Vectoren recycled, statt den sonst entstehenden "Müll" dem GC zu überlassen. vllt. noch ein Anwendungsbeispiel:
Code:
Vector2D v1 = new Vector2D(33, 35);
Vector2D v2 = new Vector2D();
v2 = v1.multiply(v2, -1);
BTW.: Ich hab' die Frage auch schon mal gehört... von was für einem Overhead redest du?
Ich weiss ja nicht inwiefern das wirklich etwas bringt aber es gibt noch immer
ein System.gc() falls du wirklich tausende von Vectoren erzeugt. Dann klatscht
du das immer mal wieder rein.
Solange man nicht vorhat, soviele Vectoren zu drehen, finde ich aber deine
Lösung auch übertrieben. kA was Andray mit "Overhead" meint, aber ich find die
Methode einfach schon mal zu kompliziert zu nutzen. Zuviel Schreibarbeit.
edit: Wobei deine Version allerdings doch ziemlich cool sein kann, wenn du
das "target" einfach optional machst. Dann muss man nich immer ein dummes "null" übergeben. Also so:
@hdi: Ja... dachte auch erst, das es das bringen müsste. Ist ja naheliegend. Aber neee... Im übrigen... Auf die Idee kam ich, als ich mir rein zufällig mal die Klasse "String" genauer angesehen habe ("subString()" usw.).
Das bedeutet, das ständig neue Vectoren erzeugt wurden. ...Irgendwann waren es zuviele... und das obige war die Lösung... Ich habe schlicht nicht mehr aktuelle Vectoren recycled, statt den sonst entstehenden "Müll" dem GC zu überlassen. BTW.: Ich hab' die Frage auch schon mal gehört... von was für einem Overhead redest du?
Ja, gut, wenn man die unbedingt recyclen will, dann erstelle man sich doch separate "recyclende" methode:
[highlight=Java]
//normale variante wenn es einfach nur schnell & einfach lesebar sein muss
public Vector2D multiply(double d){
return new Vector2D(this.x*d,this.y*d);
}
//recyclende methode die DEFINITIV ein target erwartet, und sonst NullPointer spuckt
public Vector2D multiply(double d, Vector2D target)
{
target.x = this.x * d;
target.y = this.y * d;
return target;
}
[/highlight]
Dann hätte man zwar eine GC-schonende methode, die aber nicht dauern diese sinnfreie ==null vergleiche ohne ende anstellt. Ich weiß es (wie immer) nicht, aber beim solchen extrem oft verwendeten low-level code sollte man imho auf solche if's verzichten, weil diese ganzen sprünge evtl. mehr kosten, als die berechnung selbst (das meinte ich mit "overhead").
Und dass es 2 methoden sind: naja, im "normalfall" würde ich eh lieber die einfache, nicht recyclende variante benutzen, weil die einfacher zu lesen und zu schreiben ist, dieses eine Argument könnte auf dauer lästig werden...
@0x7F800000: Wieso sinnfrei? die erste Methode in meinem beispiel ist dir aufgefallen ja? (edit: ohne dem if bringts auch nicht allzuviel mehr... grad' mal rd. 2 FPS.)
@hdi: Optional... gute Idee.
Bei meiner variante werden die beiden methoden einmal zur compilezeit unterschieden.
Bei dir werden die beiden Fälle auch unterschieden, allerdings zur laufzeit , und zwar bei jedem Aufruf mit einem if.
Rein intuitiv würde ich daher erwarten, dass meine variante zumindest ein wenig schneller ist.
Aber ne^^ :autsch: nix da.
[highlight=Java]
public class _ {
private static class SpaceratVector{
private double x;
public SpaceratVector(){}
public SpaceratVector(double x){ this.x=x; }
public SpaceratVector multiply(double d){
return multiply(d,null);
}
public SpaceratVector multiply(double d,SpaceratVector v){
if(v==null) v=new SpaceratVector();
v.x=this.x*d;
return v;
}
}
private static class AndreyVector{
private double x;
public AndreyVector(){}
public AndreyVector(double x){ this.x=x; }
public AndreyVector multiply(double d){
return new AndreyVector(this.x*d);
}
public AndreyVector multiply(double d,AndreyVector v){
v.x=this.x*d;
return v;
}
}
public static int n=Integer.MAX_VALUE/8;
public static void main(String..._){
//"aufwärmen"
for(int i=0; i<n;i++);
for(int runs=0; runs<10; runs++){
//nur zählschleife
long loopTime=System.currentTimeMillis();
for(int i=0; i<n; i++);
loopTime=System.currentTimeMillis()-loopTime;
Das ergebnis sagt absolut nichts aus. Scheint auch noch von der reihenfolge der experimente abhängig zu sein. Jedenfalls habe ich eine Konstellation gefunden, bei der Spacerat's methoden nicht nur "nicht wesentlich langsamer", sondern schlicht und einfach schneller waren... Naja, dem hdi habe ich auch schon mal scherzhaft empfohlen, mitten in den code sinnfreie while-schleifen einzubauen, um den code zu beschleunigen. :bloeddas kam bei so einer krüppeligen microbenchmark halt raus^^)
Ich habe keinen blassen schimmer Ahnung, wieso methodenaufruf+methodenaufruf+if+rechnen+return+return
schneller ist als einfach nur methodenaufruf+rechnen+return
=> ich sehe mich gezwungen jede performance-basierte argumentation gegen spacerat's vorgehensweise einzustellen. ???:L :bahnhof:
aber irgendwas in meinem Inneren protestiert dennoch^^
=> java macht ???irgendwas???, also seht einfach zu, dass der code hübsch ist und asymptotisch schnell läuft, alle kleinen optimierungen sind anscheinend vollkommen scheißegal und gehen im stochastischen rauschen unter.
Hey Andrey... mach dir nichts draus... Das "if" weglassen habe ich inzwischen auch probiert (s.o.). Bei besagtem Morph-Objekt waren es aber so ca. 300 Vertices die innerhalb eines Frames bewegt werden wollten. Hab' dafür noch mal ein paar Werte:
Ohne Recycling: 12,3 FPS (moment mal... 12,3 * 300 => 3690 Objekte pro Sekunde für den GC... und der streikt... ich kapiers bis heute nicht)
Mit Recycling und "if": 143,4 FPS
Mit Recycling ohne "if": 145,6 FPS
also peace
(edit: @Gemli: Das war sooo unhöflich von uns, hier im Anfänger-Forum so 'ne Diskussion loszureissen... sorry... aber wurde dein Problem wenigstens hinreichend gelöst?)
um das zu analysieren, sollte man sich wahrscheinlich mal den byte code beider varianten angucken. der code von andrey müsste schneller sein. allerdings tatsächlich kaum messbar.
Ich persönlich mache das so, dass die Objekte immer verändert zurückgegeben werden (ähnlich wie StringBuilder), und wer das Original doch noch braucht, muss vorher copy() aufrufen:
[highlight=Java]b = a.multiply(3.4); // direkt, a und b zeigen auf dasselbe Objekt (Rückgabewert wird eigentlich nicht benutzt)
b = a.copy().multiply(3.4); // Original bleibt in a erhalten, b bekommt die gestreckte Kopie
b = a.multiply(3.4).copy(); // a und b sind gestreckt und gleich, aber unterschiedliche Instanzen[/highlight]
Ark
edit: Wobei deine Version allerdings doch ziemlich cool sein kann, wenn du
das "target" einfach optional machst. Dann muss man nich immer ein dummes "null" übergeben. Also so:
um das zu analysieren, sollte man sich wahrscheinlich mal den byte code beider varianten angucken. der code von andrey müsste schneller sein. allerdings tatsächlich kaum messbar.
Was denn... geht das oben nicht draus hervor? Andreys Methode ist schneller!
300 Vertices pro Frame bedeutet:
mit "if": 143,4 FPS * 300 = 43020 Methodenaufrufe pro Sekunde.
ohne "if": 145,6 FPS * 300 = 43680 Methodenaufrufe pro Sekunde.
Das macht bei Andrey glatt 660 Methodenaufrufe mehr in einer Sekunde. Was dann wiederum bedeutet, dass das "if" ca. 1,5 Millisekunden zur Ausführung braucht. Selbst wenn mein FPS-Counter ständig zappelt (der Slang sollte jedem geläufig sein), ist das doch ein brauchbares Ergebnis. Zumindest weis mann wann's schadhaft wird.
Ich weiss ja nicht inwiefern das wirklich etwas bringt aber es gibt noch immer
ein System.gc() falls du wirklich tausende von Vectoren erzeugt. Dann klatscht
du das immer mal wieder rein.
Neee... das bitte nicht. Das sollte nicht notwendig sein, und ändert auch nichts am eigentlichen Problem: Wenn man die Methode 1000 mal mit 'null' aufruft, dann muss 1000 mal ein neuer Vector erstellt werden - egal ob man noch gc() aufruft oder nicht. Allerdings scheint das nicht so einen großen Unterschied zu machen, wie ich erwartet hatte (und zumindest nicht mehr so viel wie unter Java 1.4 ohne -server-Flag ).
@Andrey (den Namen wirst du akzeptieren müssen) : Microbenchmarks sind bei Just-In-Time-Optimierten Sprachen so eine Sache. Theoretisch könnte er an diesem Code fast alles wegoptimieren, weil die Ergebnisse ja nie gebraucht werden....