im Konstruktor soll überprüft werden, ob der übergebene Parameter mit dem Namen Richtung
'N' 'S' 'O' oder 'W'
ist. Wie kann ich diese Überprüfung denn am elegantesten durchführen. Überlege richtung in einen String zu casten und anschließend die Methode equals aufzurufen. Was haltet ihr davon, wäre super dankbar für ein praktisches Beispiel.
Finde ich nur halb so verrückt, abgesehen davon kann (oder muss) man sich das [c]new String()[/c] sparen. Ebenso (siehe Post von EikeB) ist es nicht [c]>[/c] sondern [c]>=[/c] 0. Also [c]return "NWSO".indexOf(c) >= 0[/c].
Aber normalerweise kommt der Wert von einer DB, von einem GUI-Feld, von einem XML Service etc etc Irgendwo kommt ein Wert immer her. Insofern muss man (meistens) wohl oder übel ein "Mapper" schreiben.
Hi,
mal abgesehen davon, dass man richtungen über Enums eleganter kapseln kann,
was ist an so einer Methode denn so böse? (Da der TO ja die Buchstaben groß geschrieben hat gehe ich mal davon aus, dass er große haben will)
Java:
publicstaticbooleanvalid(char c){if(c =='N'|| c =='S'|| c =='O'|| c =='W')returntrue;elsereturnfalse;}
Und mal ehrlich, vergleich ihr bestimmte charvorkommen echt indem ihr jedes mal ein neues Stringobjekt anlegt, das ganze dann durchrennt und dann nochmal den Wert abfragt der dabei herauskommt?
Spätestens wenn man solchen Code an Performanzbrennpunkten einbaut wird das teuer.
Uiui.
Da hatte ich (Java-Anfänger) doch ausprobiert, ob das
"NWSO".indexOf(c) >= 0
funktioniert. Das war mein erster Gedanke, doch es schien nicht zu funktionieren, was mich wunderte.
Dann hab ich andere Dinge probiert, es ging garnichts und merkte dann irgendwann, dass ich indexOf falsch geschrieben hatte. (indexof)
Da hatte ich dann eben gerade das ausprobiert, was ich dann schliesslich gepostet hatte und gleich nach dem Absenden bemerkt, dass ich das = irgendwie auch noch verschlampt hatte.
War nicht mein Tag, Sorry.
Und mal ehrlich, vergleich ihr bestimmte charvorkommen echt indem ihr jedes mal ein neues Stringobjekt anlegt, das ganze dann durchrennt und dann nochmal den Wert abfragt der dabei herauskommt?
Spätestens wenn man solchen Code an Performanzbrennpunkten einbaut wird das teuer.
Hinsichtlich der Geschwindigkeitsbehauptung (Performance) von Firephoenix hätte ich nach meinen Erfahrungen in anderen Programmiersprachen doch so meine Bedenken.
Da wäre mir ein Beweis, noch besser eine Quantifizierung ganz lieb, wenn jemand mal Lust dazu hat.
Bei mir reicht die Performance in Java dafür noch nicht .
Microbenchmarks sind schwierig, aber der ist SO Micro, dass jeder Versuch, ihm mehr Sinn zu verleihen (indem man die Ein- und Ausgabe irgendwie verwendet) aufwändiger wäre, als das, was gemessen werden soll. Noch eine Alternative (die zumindest in C theoretisch noch schneller sein könnte)
Code:
boolean result = false;
switch (c)
{
case 'N':
case 'S':
case 'O':
case 'W':
result = true;
}
Ja, richtig,
hab mal einfach die zwei Zeilen tot gelegt und da variieren die Ergebnisse auch schon bedeutend.
Ob ein Vergleich mit C dann wieder passend ist, ist auch wieder ne andere Frage.
Der direkte Vergleich ist sicher fragwürdig. Andererseits sind die bisher beschriebenen Möglichkeiten ja alle so einfach, dass man erstens davon ausgehen kann, dass da der JIT-Compiler (abgesehen von der theoretischen Möglichkeit, den kompletten Code wegen Überflüssigkeit wegzuwerfen ) nicht mehr viel dran drehen kann (also kaum noch inlinen, Terme vereinfachen Konstanten erkennen oder was er sonst noch so alles macht), und man sich zweitens recht gut vorstellen kann, was da für ein Assembler/Maschinen- bzw. Bytecode rauskommt. Lapidar gesagt: Bei den 'if's dürfte es in beiden Fällen eine sehr ähnliche Folge von CMP/JNE bzw. IFEQ-Befehlen sein, während beim switch wohl auch in beiden Sprachen was sehr ähnliches mit JMP's bzw. einem TABLESWITCH rauskommt... (aber kein C-Programmierer würde auf die Idee kommen, bei sowas mit strcmp oder einer for-Schleife über einen char* rumzupfuschen, weil's nicht nur für den Computer, sondern auch für den Programmierer aufwändiger wäre...)
Das könnte dank dem String-Pool durchaus sein. Ich kenn die Materie zwar nicht gerade gut, aber ich könnte mir vorstellen, dass dieser String ein Mal angelegt wird und danach im Pool weiterexistiert. Somit wär das wie [c]if (... || ... ...)[/c] - einfach mit einer Schleife.
Wie schon angedeutet ist es wirklich schwierig, bei solchen "kleinen" Dingen zu spekulieren, wo wirklich "Zeit verbraten" wird. Eine if-Abfrage anzuarbeiten dauert auf einem normalen PC so lange, wie ein Lichtstrahl braucht, um eine Strecke von 15 Zentimentern zurückzulegen. Wenn man dann bedenkt, dass bei dem string.indexOf eine Methode aufgerufen wird, und auf einen Array zugegriffen wird (jeder Zugriff ist eine Addition (und theoretisch(!) auch irgendwelche Bounds-Checks!?) und in einer Schleife ja selbst wieder in jedem Schritt ein Increment und ein Vergleich gemacht werden müssen, wäre der Aufwand damit vielleicht theoretisch(!) zig-mal höher als bei 4 ifs - aber dann startet man den Windows-Timer, mit siner 20-Millisekunden-Auflösung, und läßt neben dem Benchmark eine MP3 laufen während Thunderbird mal kurz beim Server anfragt ob's neue Mails gibt... (Ist ja nur eine kleine akademische Diskussion unter Nerds )
Ich kenn die Materie zwar nicht gerade gut, aber ich könnte mir vorstellen, dass dieser String ein Mal angelegt wird und danach im Pool weiterexistiert.
ja. jedes stringliteral wird permanent durch den pool referenziert und demnach wird bei der lösung auch nur ein einziges string-obj angelegt. anders als bei der einen variante mit
Code:
new
. ich denke bei der variante mit der bool-verkettung ist einfach das assoziieren der chars aufwändiger. zwischen den booleans und der indexOf sehe ich eigentlich keinen unterschied (eher negativ für indexOf).
aber das thema ist eh nicht signifikant...
aber dann startet man den Windows-Timer, mit siner 20-Millisekunden-Auflösung, und läßt neben dem Benchmark eine MP3 laufen während Thunderbird mal kurz beim Server anfragt ob's neue Mails gibt...
importjava.util.Random;publicclassRandomJunk{publicstaticbooleantest1(char c){return c =='N'|| c =='S'|| c =='O'|| c =='W';}publicstaticbooleantest2(char c){return"NSWO".indexOf(c)!=-1;}privatestaticRandom random;publicstaticvoidresetChars(long seed){
random =newRandom(seed);}publicstaticchargetNextChar(){return(char)(65+random.nextInt(25));}publicstaticvoidmain(String[] args){finalint TESTS =1000000;finalint TEST_RUNS =5;long seed =System.nanoTime();for(int n =0; n < TEST_RUNS; n++){resetChars(seed);long time =System.currentTimeMillis();for(int i =0; i < TESTS; i++){test1(getNextChar());}System.out.println("test1 took "+(System.currentTimeMillis()-time)+"ms !");resetChars(seed);
time =System.currentTimeMillis();for(int i =0; i < TESTS; i++){test2(getNextChar());}System.out.println("test2 took "+(System.currentTimeMillis()-time)+"ms !");System.out.println("-----------------------------");}}}
Ausgabe bei mir:
Code:
test1 took 41ms !
test2 took 48ms !
-----------------------------
test1 took 39ms !
test2 took 47ms !
-----------------------------
test1 took 39ms !
test2 took 47ms !
-----------------------------
test1 took 39ms !
test2 took 48ms !
-----------------------------
test1 took 39ms !
test2 took 47ms !
-----------------------------
Kann dein Ergebnis also nicht nachvollziehen, wäre auch merkwürdig.
EDIT:
Ich liebe es, wenn ich übersehe, dass da noch so eine ganze Seite Diskussion ist.
Egal, ich stimme zu, dass ein solcher Benchmark fast schon zu micro ist um was auszusagen, außer dass jegliche Optimierung dieses Vergleiches Zeitverschwendung ist, aber nichtsdestotrotz kann ich nicht nachvollziehen wie der test 1 jemals langsamer sein sollte als der Test 2.
test1 took 62ms !
test2 took 51ms !
-----------------------------
test1 took 32ms !
test2 took 29ms !
-----------------------------
test1 took 18ms !
test2 took 29ms !
-----------------------------
test1 took 18ms !
test2 took 27ms !
-----------------------------
test1 took 19ms !
test2 took 25ms !
-----------------------------
test1 took 47ms !
test2 took 46ms !
-----------------------------
test1 took 32ms !
test2 took 31ms !
-----------------------------
test1 took 31ms !
test2 took 31ms !
-----------------------------
test1 took 32ms !
test2 took 31ms !
-----------------------------
test1 took 31ms !
test2 took 31ms !
-----------------------------
Und wenn man den Inhalt beider test-Methoden durch "return true" ersetzt ist's
Code:
test1 took 31ms !
test2 took 32ms !
-----------------------------
test1 took 31ms !
test2 took 31ms !
-----------------------------
test1 took 16ms !
test2 took 31ms !
-----------------------------
test1 took 31ms !
test2 took 16ms !
-----------------------------
test1 took 31ms !
test2 took 31ms !
-----------------------------
Der Versuch, dem ganzen einen Hauch eines Sinnes zu geben (obwohl er immernoch optimieren kann, was er will, weil die boolean-Ergebnisse nicht sinnvoll verwendet werden (können)) :
Java:
publicclassRandomJunk{publicstaticbooleantest1(char c){return c =='N'|| c =='S'|| c =='O'|| c =='W';}publicstaticbooleantest2(char c){return"NSWO".indexOf(c)!=-1;}publicstaticbooleantest3(char c){switch(c){case'N':case'S':case'W':case'O':returntrue;}returnfalse;}publicstaticvoidmain(String[] args){for(int n =0; n <25; n++){long before, after;int m =10000000;
before =System.nanoTime();for(int i=0; i<n*m; i++){test1('N');test1('S');test1('W');test1('O');}
after =System.nanoTime();System.out.println("run "+n+" test1 took "+(after-before)/1e6);
before =System.nanoTime();for(int i=0; i<n*m; i++){test2('N');test2('S');test2('W');test2('O');}
after =System.nanoTime();System.out.println("run "+n+" test2 took "+(after-before)/1e6);
before =System.nanoTime();for(int i=0; i<n*m; i++){test3('N');test3('S');test3('W');test3('O');}
after =System.nanoTime();System.out.println("run "+n+" test3 took "+(after-before)/1e6);System.out.println("");}}}
Wie zu erwarten war, ist das zumindest ein starkes Indiz dafür, dass man im Zweifelsfall lieber beim if bleiben sollte...
switch-case ist bei diesen Sachen nicht schneller? char besteht ja aus 16 Bit und wird intern vielleicht mit 32 Bit umgesetzt, vielleicht verschafft das dem switch einen Vorteil?
Abgesehen davon müsste theoretisch binary search eine etwas bessere Laufzeit erzielen ode nich? 2 vs 2,5?
Lange Rede, kurzer Sinn: auf gut deutsch: scheiss auf alle diese Microbenchs und um Überlegungen zur Geschwindigkeit. Abgesehen davon sollte die Laufzeit bei allen Beispielen [c]O(n)[/c] sein, wobei [c]n[/c] die Anzahl gültiger Werte ist. Erst wenn man etwas mit [c]O(n^2)[/c] implementiert, sollte man sich Gedanken machen Einfach das coden, was intuitiv und einfach ist
Ganz so würde ich das nicht unterschreiben. Einerseits sind in den meisten Fällen sind Fragen wie Übersichtlichkeit und Flexibilität wichtiger, aber wenn f(n) = n und g(n) = 3600*n sind, sind die beide in O(n), aber ob man eine Sekunde wartet oder eine Stunde macht schon einen Unterschied. WENN man also feststellt, dass das eigene Programm eine Stunde braucht, und diese Zeit ausschließlich in einer Schleife verbraten wird, die im Kern nur aus "QWOP".indexOf(c) besteht, dann könnte man ja mal überlegen und vielleicht einen Benchmark machen, bei dem man schon gezielt darauf achtet, dass er repäsentativ ist, z.B. indem man das Wegwerfen der Ergebnisse vermeidet, verfälschende JIT-Optimierungen schwieriger macht, oder auch andere Buchstaben verwendet, etwa mit
Java:
publicclassRandomJunk{publicstaticbooleantest1(char c){return c =='N'|| c =='S'|| c =='O'|| c =='W';}publicstaticbooleantest2(char c){return"NSWO".indexOf(c)!=-1;}publicstaticbooleantest3(char c){switch(c){case'N':case'S':case'W':case'O':returntrue;}returnfalse;}publicstaticvoidmain(String[] args){for(int n =0; n <25; n++){long before, after;long sum =0;int m =1000000;char input[]={'N','S','O','W','X','X','X','X'};
sum =0;
before =System.nanoTime();for(int i=0; i<n*m; i++){for(int j=0; j<input.length; j++){if(test1(input[j])) sum++;}}
after =System.nanoTime();System.out.println("run "+n+" sum "+sum+" test1 took "+(after-before)/1e6);
sum =0;
before =System.nanoTime();for(int i=0; i<n*m; i++){for(int j=0; j<input.length; j++){if(test2(input[j])) sum++;}}
after =System.nanoTime();System.out.println("run "+n+" sum "+sum+" test2 took "+(after-before)/1e6);
sum =0;
before =System.nanoTime();for(int i=0; i<n*m; i++){for(int j=0; j<input.length; j++){if(test3(input[j])) sum++;}}
after =System.nanoTime();System.out.println("run "+n+" sum "+sum+" test3 took "+(after-before)/1e6);System.out.println("");}}}
Und wenn man dann sieht, dass da sowas wie
run 11 sum 44000000 test1 took 293.19562
run 11 sum 44000000 test2 took 1305.073507
run 11 sum 44000000 test3 took 474.193816
steht, und dann mal im Programm die if-Variante verwendet und es dann nicht mehr eine Stunde braucht, sondern nur 15 Minuten, war es das ja vielleicht doch wert...
Gut gemacht, Marco,
ich konnt es natürlich nicht lassen damit ein wenig zu experimentieren.
Es scheint, dass sogar eine (geringfügige) Rolle spielt, in welcher Reihenfolge die "test"s ausgeführt werden. Wenn ich z.B. immer nur test1 aufrufe, ist bei mir der jeweils mittlere der schnellste.
Ich habe dann auch die Abfrage auf 12 verschiedene Zeichen ausgedehnt und da scheinen sich switch-case und if nicht mehr zu unterscheiden. indexOf ist auch da deutlichst im Nachteil.
Was die Lesbarkeit angeht.. naja.
Man kann ja nicht alles haben, nichtwahr.
[...] und dann mal im Programm die if-Variante verwendet und es dann nicht mehr eine Stunde braucht, sondern nur 15 Minuten, war es das ja vielleicht doch wert...
Da sprechen wir aber auch von "zeitkritischem" Code, wobei Zeitkritisch bei 15 vs. 60min in Klammern geschrieben werden muss Obwohl, eigentlich muss dieser Code nicht mal zeitkritisch sein, er könnte dir einfach zu langsam sein.
Ich kenne das auch: Code auf dem Mainframe, welcher die 1/n-Jahresendverarbeitung bis am Morgen bei Geschäftsbeginn durchlaufen muss
Aber ganz ehrlich: wie viel % von produktivem Code ist zeitkritisch? 3%? 2%?
Natürlich ist String#indexOf() eher ein Juxbeispiel, aber ist es nicht schön zu lesen?
Der Prozentsatz hängt davon ab, was man macht. Bei allem, was mit Grafik oder allgemein großen Daten (HPC usw.) zu tun hat, ist es klar. Und wie gesagt sollte man das nur machen, nachdem man das Bottleneck z.B. durch gewissenhafte Messungen oder Profiling identifiziert hat.
Wenn nur n, o, s, w auftauchen, ist die Laufzeit von if-else und switch durchschnittlich = 2,5. Bei binary search = 2*c ! Dieses c führt aber dazu, dass die Laufzeit, größer wird. : (
Was für statische Werte sollen das denn sein? Normalerweise macht man das mit Landau-Symbole ? Wikipedia. Unsere Methoden haben eine Laufzeit von [c]O(n)[/c]. Binary Search AFAIK [c]O(n * log(n))[/c]
Wenn man wiederholt Eingaben mit einer gewissen Wahrscheinlichkeiten hat, macht es Sinn, über die Durchschnittliche Laufzeit zu sprechen und auch binary search in Betracht zu ziehen imho, das meint ich
Ach ja natürlich, hang irgendwie bei Quick Sort, welches [c]O(n * log(n))[/c] hat. Wär bei einer Suche etwas katastrophal, wenn sie länger als linear ist