Tag Archive: Java


Bevor ich wieder in einen monatelangen Java-Beitrags-Winterschlaf falle und erneut nur über TV-Serien und alte Spiele schreibe, möchte ich hier noch einen kleinen Anfängertipp geben. Wieso eigentlich? Ganz einfach weil ich in letzter Zeit so einigen ungeübten Java-Programmierern über die Schulter sehen durfte, die alle völlig unvorbereitet in die im Folgenden thematisierte Falle tappen. Scheinbar ist Java in dieser Hinsicht wirklich so unintuitiv, dass es jedem am Anfang passiert, und ich erinnere mich, dass es mir im Studium genauso erging. Ich erkannte das Problem recht schnell und hatte auch sofort eine Lösung parat, die ich seitdem schon ganz automatisch verwende, aber vielleicht fällt das nicht jedem so leicht.

Die Situation ist schnell erklärt: Mittels Zählschleife soll über eine Collection, eine Liste, iteriert werden, die irgendwelche Elemente enthält. Diese erweiterte For-Schleife dient dazu, nicht benötigte Elemente aus der Collection zu entfernen, also eine Art Filterung soll vorgenommen werden. Am Ende verbleiben nur solche Elemente in der Collection, mit denen wir weiterarbeiten wollen. Aus Gründen der Einfachheit sind das in diesem Fall keine komplexen Datentypen, sondern Strings:

Sieht einfach aus, ist es aber dann doch nicht. Der Code produziert nämlich eine ConcurrentModificationException. Diese Exception ist eigentlich dazu da, zu verhindern, dass eine Collection zeitgleich modifiziert wird, während gerade (z.B. in einem anderen Thread) darüber iteriert wird. Wie man sieht, braucht es allerdings gar keine anderen Threads, um die Exception zu provozieren. Wir dürfen nicht gleichzeitig iterieren und Elemente einfügen oder entfernen. Was kann ich also sonst tun, damit ich meine Elemente wegbekomme?

Eine naheliegende Lösung ist, sich die entsprechenden Elemente einfach bis dahin zu „merken“, um sie erst nach dem Iterieren zu entfernen. Alternativ könnte man sich aber auch nur die Elemente merken, die man behalten möchte, und dabei einfach eine neue Liste generieren. Welche der beiden Methoden nun besser ist, hängt davon ab, ob nach dem Entfernen mehr Elemente zurückbleiben als entfernt werden. Sowas lässt sich aber natürlich nicht immer abschätzen, darum verwende ich meistens pauschal die subtraktive Variante:

Mit der Methode removeAll() gibt es sogar gleich das ideale Werkzeug für solche Operationen. Ich benötige diesen und ähnlichen Code bei meiner Arbeit immer mal wieder. Erfahrene Java-Entwickler wissen natürlich, dass man in solchen Fällen am besten gleich einen richtigen Iterator verwendet, weil man mit diesem auf sichere Weise Elemente beim Iterieren entfernen kann. Ich bin allerdings auch gleichzeitig ein Fan der leicht verwendbaren For-Each-Schleifen, daher bin ich da meist ein wenig im Konflikt. Ist meiner Meinung nach aber auch alles kein Problem, solange man weiß, wie man damit umgehen muss.

„Wie bitte? Sound in Java? Wer würde denn so etwas wollen? Und dann wahrscheinlich auch noch 100% plattformunabhängig, ja? Wer Sound in Java haben will, soll wenigstens furchtbar leiden müssen. Also gut, wir machen es! Aber wir machen es so schlecht wie möglich, damit es niemand benutzen will!“

So oder so ähnlich muss das wohl bei Sun geklungen haben, als man sich irgendwann notgedrungen entschloss eine Java Sound API zu entwickeln. Ich kann mir jedenfalls nicht anders erklären, wieso es im Jahr 2012 immer noch nicht möglich ist, anständig mit JavaSound zu arbeiten, wenn es um so simple Dinge wie Ausgabe von z.B. WAVE-Audio geht. Es funktioniert – keine Frage – aber es macht keinen Spaß und es ist eine performancetechnische Katastrophe.

Gestraft ist man vor allen Dingen dann, wenn man sich aus Gründen des Minimalismus und im Sinne der größtmöglichen Platzersparnis entschließt, weitestgehend unabhängig von den bekannten teilweise mehrere Megabyte großen Java-Soundbibliotheken irgendwelcher Fremdanbieter bleiben zu wollen, die vereinzelt scheinbar sogar 3D-Audio in Java realisieren können. Aber sowas wollte ich gar nicht. Ich wollte einfach nur Audiodateien ausgeben ohne zusätzlichen Ballast von irgendwelchen Multimediabibliotheken und Java-Spiele-Komplett-Engines einbinden zu müssen. Ich bräuchte sowieso nur Start, Stopp und einstellbare Loops, mehr nicht. Zum Glück kann das JavaSound alles. Theoretisch.

Man hat die Wahl der Qual zwischen Pest und Cholera. Es gibt java.applet.AudioClip, welches den Vorteil hat, wirklich extrem einfach zu bedienen zu sein. Man kann da gar nichts falsch machen. Leider funktioniert das mit vielen WAVE-Dateien nicht, wenn sie z.B. eine etwas exotischere Samplingfrequenz haben. Auch gibt es Probleme mit sehr kurzen oder sehr langen Audiodateien. Und das Loopen und Abspielen ist mir dann eigentlich doch zu spartanisch. Looppoints wären schon nötig gewesen.

Für so etwas gibt es JavaSound mit javax.sound.sampled.Clip. Funktioniert ähnlich einfach, und bringt gleich die nötigsten Methoden mit, man kann sogar „Vor- und Zurückspulen“ und die Anzahl der Loops setzen. Clip leidet allerdings unter einer unglaublich miesen Performance. Das macht sich beim Abspielen eines einzelnen Samples gar nicht bemerkbar, außer man hält den Speicher- und CPU-Verbrauch seiner JVM sehr genau im Auge. Nein, das ganze Ausmaß des Grauens entdeckt man erst, wenn man – wie das z.B. in Spielesoftware recht üblich ist – den selben Clip in sehr kurzen Abständen mehrfach hintereinander abspielt (z.B. Sprung- oder Schieß-Geräusche). Die ganze Anwendung beginnt zu ruckeln, die Sounds werden teilweise verschluckt, manchmal überhaupt nicht abgespielt. Das ist so natürlich nicht akzeptabel. Ich vermute es liegt einfach am nicht unerheblichen Overhead für das Starten und Stoppen der Threads jedes einzelnen Sounds.

Es gab da noch eine weitere Kleinigkeit, die mir die Haare zu Berge stehen ließ. Bei Clip lassen sich zwar bequem die Looppunkte setzen, beim ersten Abspielen mittels loop() scheint es diese aber komplett zu ignorieren. Erst beim zweiten und bei jedem weiteren Mal geht es dann plötzlich wie gewünscht. Ich habe alle erdenklichen Kombinationen versucht, den Audiopuffer geleert, und alle Methoden getestet, die diese Klasse mit sich bringt. Da war nichts zu machen. Ich musste mir schließlich mit einem billigen Workaround behelfen, das Sample zuvor „heimlich“ einmal für eine Millisekunde anzuspielen und sofort mit anschließendem Rewind zurücksetzen, damit es dann gleich beim ersten Mal richtig loopt.

JavaSound hat noch soviele Kinderkrankheiten, dass es beinahe schon absurd ist, wenn man bedenkt, wie lange es Java schon gibt. Sogar die deutlich jüngere Dalvik VM unter Android bietet mehr und leistet mehr, ist vor allem sehr viel spielefreundlicher in dieser Hinsicht. Multimedia in Java ist wohl leider noch etwas, das äußerst stiefmütterlich von Sun bzw. Oracle behandelt wird. Okay, dafür gibt es ja wenigstens JavaFX 2.0. Vielleicht taugt das ja für solch verrückte und völlig abwegige Experimente wie Soundausgabe in einer Java-Anwendung.

Ich dachte bis heute morgen noch, ich sei mittlerweile lange genug Java-Entwickler, um vor solchen blöden Fehlern gefeit zu sein. Stellt sich heraus, dass man wirklich viel Zeit mit sinnloser Fehlersuche selbst in simpelstem Code verschwenden kann.

Heute schrieb ich etwas in meiner favorisierten Java-IDE Eclipse, das ich in der Art praktisch täglich unzählige Male schreibe:

List<String> myList = new ArrayList<String>();

List und ArrayList wurden mir rot unterstrichen, weil er die Klassen nicht kannte. Es dauert natürlich nur wenige Klicks und Eclipse generiert die fehlenden Imports blitzschnell. So weit so normal. Doch dann wird mir List erneut rot unterstrichen, mit der Fehlermeldung „The type List is not generic; it cannot be parameterized with arguments <String>„.

Ungläubig starrte ich die Fehlermeldung an. Ich war mir eigentlich total sicher, dass List Generics verwendet. Sicherer ging es fast nicht. Die Fehlermeldung sagte also genau das Gegenteil von dem aus, was meiner Ansicht nach den Tatsachen entsprechen sollte. Oder doch nicht? Plötzlich begann ich zu zweifeln. Werde ich etwa schon senil? Wieso ist ArrayList parametrisierbar, aber List auf einmal nicht? Ist das irgendwie sinnvoll?

Ich begann hilflos, die Zeile irgendwie zu modifizieren. War List auf einmal allergisch gegen String geworden? Nein, daran lag es nicht, auch andere Parameter nahm er nicht an. Ich hätte den Parameter weglassen können, oder ich hätte auch direkt ArrayList anstelle von List verwenden können, das hätte funktioniert, aber Eclipse sollte mich doch nicht etwa dazu kriegen, die wichtigsten Programmierparadigmen zu vergessen. Ich vermutete, dass Eclipse einfach mal wieder seine Tage hatte und mich zu Unrecht irgendwelcher Anfängerfehler beschuldigte. An der Zeile war alles in Ordnung, postulierte ich.

Ich begann also doch das Orakel von Google zu bemühen, weil ich offenbar außer Stande war, mir selbst bei solch einem trivialen Problem zu helfen. Eine Quelle behauptete nun, dass es wohl mit dem Compliance-Level des Compilers zu tun hatte, das versehentlich auf eine Version vor Java 1.5 gestellt sein musste, also bevor Generics in Java eingeführt wurden. Ein kurzer Blick in die Compiler-Optionen des Projekts widerlegte das. Mit dem Compliance-Level war alles in Ordnung. In Zukunft also wieder auf konkrete Implementierungen programmieren?

Zum Glück nicht, denn des Rätsels Lösung fand ich direkt im Anschluss, und ich bin erschrocken über die Tatsache, dass ich an dieser Stelle vielleicht erst ganz zuletzt gesucht hätte. Ich habe mich bei den Imports entweder verklickt oder Eclipse hat mich einfach nur auf den Arm nehmen wollen. Als ich List importieren wollte, habe ich wie automatisch die oberste Option ausgewählt, weil das in dem Fall normalerweise immer funktioniert. Ausnahmsweise dachte Eclipse, es wäre besonders witzig wenn ganz oben die wenig bekannte java.awt.List stehen würde, anstelle der sehr viel häufiger genutzten gleichnamigen Klasse java.util.List.

Nun, die AWT-List verwendet tatsächlich keine Generics, Eclipse hat also Recht behalten. Aber der Fehler ist so dämlich, dass es mir eine Lehre war, die Imports allzu leichtfertig zu handhaben. Künftig prüfe ich höchstpersönlich jede einzelne importierte Klasse auf Richtigkeit.

Zur Zeit befasse ich mich (wieder) privat und beruflich mit dem Thema „Entwurfsmuster“ („Design Patterns„). Meine letzten Versuche, diese Thematik anzugehen, waren zaghaft und wenig erfolgreich. Zu trocken und zu abstrakt. Ich hatte nie das Gefühl, dass ich irgendwas davon irgendwo mal hätte einsetzen können. Inzwischen habe ich den Entschluss gefasst, mir die Entwurfsmuster jetzt endgültig gedanklich anzueignen und damit zu arbeiten. Das bekannte Werk „Entwurfsmuster von Kopf bis Fuß“ („Head First Design Patterns„) bringt dem Leser die einzelnen Muster auf humorvolle bildhafte Weise näher. Ich muss sagen, das gelingt wirklich sehr gut. Zum ersten Mal habe ich den Eindruck, dass ich es verstehe und daraus einen Vorteil ziehen kann.

Vielleicht hatte ich deshalb Schwierigkeiten, mich initial damit anzufreunden, weil die Entwurfsmuster bei mir für ein mittelschweres Trauma gesorgt hatten. Dieses genoss ich vor eineinhalb Jahren, kurz nach meinem Studium, das ich mit einem breiten Grinsen und vor Stolz geschwellter Brust abschloss, und eine Welt erwartete, die taufrische Informatiker mit offenen Armen, einem Glas Sekt, und dem roten Teppich empfangen würde. Damals, in meiner Experimentierfreude, bewarb ich mich bei einer Spieleentwicklerfirma als Junior-Developer für Tools. Freudig erzählte ich davon, dass ich mich für den Richtigen für den Job halte, weil ich viel Erfahrung mit Spielen habe, weil ich das Spiele-Repertoire des Unternehmens kenne und selbst spiele, und ganz besonders weil ich selbst schon etwas Erfahrung im Bereich Spieleentwicklung gesammelt habe, sowohl 2D als auch 3D, sowohl Windows als auch Linux, sowohl Java als auch C++. Bestimmt würde man mir gleich den Arbeitsvertrag rauslegen.

Stattdessen fragte man mich ziemlich ausführlich, ob mir Erich Gamma ein Begriff ist, ob ich weiß wer die Gang of Four ist und was die eigentlich gemacht haben, ob ich irgendwelche Design Patterns kenne und möglichst auch gleich beschreiben könnte, wofür die da sind. Observer hier, Factory da, Design Patterns dort und überall. Ich musste bei jeder Frage abwinken und saß am Ende nur noch mit einem roten Kopf da, wollte nur noch, dass es endlich aufhört. Der Rest des Bewerbungsgesprächs verlief nicht minder ernüchternd. Lauter Begriffe, die ich vielleicht mal gehört hatte, oder eben nichtmal das. Es ging spätestens jetzt nicht mehr darum, ob ich die Stelle bekommen würde oder nicht. Die haben es nämlich geschafft, mir den Job auszureden. Ich wollte die Stelle gar nicht mehr. Ich begann zu zweifeln, ob ich als Entwickler überhaupt geeignet bin. Vielleicht hätte ich als Maurer mehr Glück.

Ich bin aus dem Informatikstudium entlassen worden, ohne dass der Begriff „Entwurfsmuster“ jemals in einer Vorlesung gefallen wäre. Java, PHP, C und C++ waren im Studium eigentlich nicht zu kurz gekommen und auch gab es die eine oder andere Vorlesung zum Thema Software Engineering, wo dann so Dinge wie das Wasserfall-Modell und das V-Modell durchgenommen wurden. Meine Noten waren zumeist überdurchschnittlich. Ich hatte nie Anlass gehabt mich schlecht vorbereitet zu fühlen. In meiner Freizeit hatte ich durchaus das eine oder andere kleine Progrämmchen oder Spielchen entwickelt, aber auch da stieß ich nie auf Entwurfsmuster, wenn ich mal was recherchierte. Und dann stand ich da, mit einem Diplom in der Hand, ohne Kenntnis von Entwurfsmustern. Und so jemanden wollen die Arbeitgeber wohl nicht.

Ich hatte Glück, ich bin nach zehnmonatiger Stellensuche gnädigerweise doch noch in einem IT-Unternehmen angestellt worden. Was sprach am deutlichsten gegen meine Anstellung: die zehnmonatige Stellensuche – „Grenzwertig“, laut Personaler. Dass ich unerfahren bin und keine Entwurfsmuster kenne, ist unproblematisch und lässt sich ändern. Warum nicht gleich so? Dann wären mir auch zehn Monate Unsicherheit erspart geblieben. Hätte meine Arbeitslosigkeit länger als ein Jahr angedauert, wer weiß ob mir mein Diplom dann noch genützt hätte.

Aus der Kategorie „zu doof für Mathematik, aber intelligent genug um sich trotzdem was einfallen zu lassen“: Heute habe ich mir mal wieder ausgiebig meinen alten Quellcode für meine Spacola-Maussteuerung vom August 2010 angesehen und mich daher auch mal wieder ein bisschen mit den mathematischen Grundlagen (Trigonometrie und Vektorarithmetik) auseinandergesetzt. Ein wenig Schmunzeln musste ich über meine unkonventionelle Lösung eines alten Problems, das ich damals hatte.

Man nehme einen Mauszeiger, der irgendwo in einem karthesischen Koordinatensystem liegt. Nun musste ich herausfinden, welchen Winkel der Richtungsvektor Ursprung->Mauszeiger mit der Y-Achse einschließt. Da ich selten mit der Mathematik einer Meinung war, aber dafür umso mehr gewillt, diese Aufgabe von alleine zu lösen, nahm ich mir seinerzeit Papier und Bleistift zur Hand und fing damit an, mir eine Lösung zu überlegen. Und ich fand einen Algorithmus, mit dem ich den Winkel näherungsweise bestimmen konnte.

Aber zunächst zur idealen und simpelsten Lösung:

Man benötigt im Prinzip nur Delta X und Delta Y zwischen den beiden Punkten, also den Ortsvektor. Anschließend teilt man Delta Y durch den Betrag des Vektors und berechnet aus diesem Wert den Arkus-Kosinus. Das Ergebnis muss dann noch mit 360 multipliziert und wieder durch 2 Pi geteilt werden. Schnell und effizient. Ich bin kein Mathegott, aber ein paar Minuten Suchen hat mich auf die richtige Lösung gebracht.

Witzig fand ich dagegen meinen Algorithmus, der folgendermaßen aussieht:

Ich habe beliebig viele Punkte (im Extremfall 360 Stück, in meinem Fall aber nur 40) auf einem Einheitkreis um den Mittelpunkt generiert und von jedem dieser gedachten Punkte den Abstand zur Mauszeigerkoordinate bestimmt. Mein gedanklicher Ansatz war der, dass der Punkt, der den kürzesten Abstand zur Zielkoordinate hat, mehr oder weniger exakt denselben Winkel zur Y-Achse hat. Mit dem Vorteil, dass ich dessen Winkel (da selbst generiert) genau kannte. Anschließend habe ich noch eine merkwürdige Umrechnung vom Bogenmaß ins Gradmaß durchgeführt. Das Ergebnis war zufriedenstellend.

Für letzteres Codebeispiel schlagt mich bitte nicht. Ich wusste damals schon, dass das nur ein Provisorium ist. Ich weiß noch nicht einmal mehr wofür „targetCnt“ steht.

Zugegeben, meine Lösung ist extrem unperformant und relativ ungenau, aber nichts stärkt das Ego mehr als die Gewissheit, ein Problem selbst und ohne Hilfe bewältigt zu haben, wenn man es sich vorher nicht zugetraut hätte. Wie dem auch sei, heute habe ich meinen provisorischen Algorithmus abgelöst und durch die exakte Berechnung ersetzt.