Schnelle Vergleiche mit CollatorKeys

Landestypische Vergleiche können mit der Collator-Klasse gemacht werden. Eine Geschwindigkeitssteigerung ergibt sich durch Collator-Keys. Obwohl sich mit der Collator Klasse sprachspezifische Vergleiche korrekt umsetzen lassen, ist die Geschwindigkeit gegenüber einem normalen String-Vergleich schlechter. Daher bietet die Collator Klasse die Objektmethode getCollationKey() an, die ein CollationKey Objekt liefert, der schnellere Vergleiche zulässt.

Collator col = Collator.getInstance( Locale.GERMAN );
CollationKey key1 = col.getCollationKey( "ätzend" );
CollationKey key2 = col.getCollationKey( "Bremsspur" );

Durch CollationKeys lässt sich die Performance bei Vergleichen zusätzlich verbessern, da der landesspezifische String in einen normalen Javastring umgewandelt wird, der dann schneller verglichen werden kann. Dieses bietet sich zum Beispiel beim Sortieren einer Tabelle an, wo mehrere Vergleiche mit dem gleichen String durchgeführt werden müssen. Der Vergleich wird mit compareTo(CollationKey) durchgeführt. Der Vergleich von key1 und key2 im Beispiel lässt sich demnach durch folgende Zeile ausdrücken:

int comp = key2.compareTo( key1 );

Das Ergebnis ist wie bei der compare() Methode bei Collator Objekten <0, 0 oder >0.

class java.text.CollationKey implements Comparable

  • int compareTo( CollationKey target )

    Vergleicht zwei CollationKey Objekte miteinander.
  • int compareTo( Object o )

    Vergleicht den aktuellen CollationKey mit dem angegeben Objekt. Ruft compareTo((CollationKey)o) auf.
  • boolean equals( Object target )

    Testet die beiden CollationKey Objekte auf Gleichheit.
  • String getSourceString() 
    Liefert den String zum CollationKey.
  • int hashCode()

    Berechnet den Hashcode für den CollationKey.
  • byte[] toByteArray()

    Konvertiert den CollationKey in eine Folge von Bits.

abstract class Collator implements Comparator, Cloneable

  • abstract CollationKey getCollationKey( String source )

    Liefert einen CollationKey für den konkreten String.

Bildfilter mit AWT-ImageFilter

ImageFilter liegen zwischen Produzenten und Konsumenten und verändern Bildinformationen oder nehmen Einfluss auf die Größe. Für Bild-Produzenten treten die Filter als Konsumenten auf, die die Schnittstelle ImageConsumer implementieren und die wichtige Methode setPixel() programmieren.

Grundlegende Eigenschaft von Filtern

Um einen Filter anzuwenden, nutzen wir die Klasse FilteredImageSource. Im Konstruktor geben wir das Bild und den Filter an. Anschließend können wir den zurückgegebenen Produzenten an createImage() übergeben, und wir haben ein neues Bild.

Beispiel: Anwendung eines Filters:

Image src = getImage( "gatesInAlbuquerque.jpg" );
ImageFilter colorfilter = new GrayFilter();
ImageProducer imageprod = new FilteredImageSource( src.getSource(), colorfilter );
Image img = createImage( imageprod );

Konkrete Filterklassen

Es gibt einige Unterklassen der Klasse ImageFilter, die für unsere Arbeit interessant sind:

  • BufferedImageFilter. Diesem Filter lässt sich ein Objekt vom Typ BufferedImageOp übergeben, mit dem unterschiedliche Manipulationen ermöglicht werden. BufferedImageOp ist eine Schnittstelle, die von AffineTransformOp, ConvolveOp, BandCombineOp und LookupOp implementiert wird. AffineTransformOp ist am attraktivsten, da es mit einem AffineTransform konstruiert wird, sodass leicht Vergrößerungen oder Rotationen ermöglicht werden. Über AffineTransform-Objekte erfahren wir im 2D-Abschnitt mehr.
  • CropImageFilter. Bildteile werden herausgeschnitten.
  • ReplicateScaleFilter. Zum Vergrößern oder Verkleinern von Bildern. Ein einfacher Algorithmus wird angewendet. Eine weiche Vergrößerung oder Verkleinerung lässt sich mit der Unterklasse AreaAveragingScaleFilter erreichen.
  • RGBImageFilter. Dieser allgemeine Filter ist für die eigene Filterklasse gedacht. Wir müssen lediglich eine filterRGB()-Methode angeben, die die RGB-Bildinformationen für jeden Punkt (x,y) modifiziert. Benötigt der Filter auch die Nachbarpunkte, können wir nicht mit RGBImageFilter arbeiten.
  • Beispiel: Ein Filter, der den Rot- und Blauanteil in einem Bild vertauscht.

    class RedBlueSwapFilter extends RGBImageFilter
    {
     public RedBlueSwapFilter()
     {
      canFilterIndexColorModel = true;
     }
     public int filterRGB( int x, int y, int rgb )
      {
        return (   (rgb & 0xff00ff00)
                | ((rgb & 0xff0000) >> 16)
                | ((rgb & 0xff) << 16));
      }
    }

    Mit CropImageFilter Teile ausschneiden

    Mit CropImageFilter lassen sich Teile des Bilds ausschneiden. Wir definieren dafür vom Bild einen Ausschnitt mit den Koordinaten x, y und der Breite und Höhe. Wie die anderen Bildfilter, so wird auch CropImageFilter mit dem FilteredImageSource als Produzent verwendet.

    Beispiel Erzeuge für die Grafik big.gif in einem Applet ein neues Image-Objekt. Das Original hat die Größe 100 × 100 Pixel. Das neue Bild soll einen Rand von 10 Pixeln haben.

    Image origImage = getImage( getDocumentBase(), "big.gif" );
    ImageFilter cropFilter = new CropImageFilter( 10, 10, 90, 90 );
    Image cropImage = createImage( new FilteredImageSource(origImage.getSource(),cropFilter) );

    Bildausschnitte über PixelGrabber ausschneiden

    Nicht nur über CropImageFilter lassen sich Bildausschnitte auswählen. Eine andere Lösung geht über PixelGrabber, da dieser auch einen Ausschnitt erlaubt. Darüber lässt sich dann mit MemoryImageSource wieder ein neues Bild erzeugen.

    Beispiel: Schneide aus dem Image img das passende Rechteck mit den Startkoordinaten x, y und der Breite width und der Höhe height aus:

    int[] pix = new int[width * height];
    PixelGrabber pg = new PixelGrabber( img, x, y, width, height,
                                      pix, 0, width );
    try {
      pg.grabPixels();
    }
    catch( InterruptedException e ) {}
    newImg = createImage(new MemoryImageSource(width, height, pix, 0, width) );

    An dieser Stelle sollten wir noch einmal den Unterschied zwischen den beiden Möglichkeiten betonen. PixelGrabber implementiert die Schnittstelle ImageConsumer, sodass er ein Bildkonsument ist und Daten in einem Integer-Feld ablegt. CropImageFilter ist ein Filter, der ein anderes Image-Objekt konstruiert und kein Feld.

    Transparenz

    Um eine bestimmte Farbe eines Bilds durchsichtig zu machen (also die Transparenz zu bestimmen), nutzen wir einen RGBImageFilter. Dabei implementieren wir einen Konstruktor, der die Farbe sichert, die transparent werden soll. Sie wird später in der Implementierung von filterRGB() verwendet. Die Methode, die ja für jeden Bildpunkt aufgerufen wird, liefert dann entweder die Farbe ohne Alpha-Kanal zurück (rgb|0xff000000) oder eben nur den Alpha-Kanal (rgb & 0xffffff) für Transparenz. Eine interessante Erweiterung ist die Einführung einer Toleranzauswertung um einen »Zauberstab«, der ähnlich wie in Photoshop zu realisieren ist.

    import java.awt.*;
    import java.awt.image.*;
    
    public class TransparentFilter extends RGBImageFilter
    {
     private final int transparentRGB;
    
     public TransparentFilter( Color color )
     {
      this.transparentRGB = color.getRGB();
     }
    
     @Override
     public int filterRGB( int x, int y, int rgb )
     {
      if ( rgb != transparentRGB )
        return rgb | 0xff000000;
    
      return rgb & 0xffffff;    //transparent
     }
    }

    Ausgaben mit MessageFormat formatieren

    MessageFormat ist eine konkrete Unterklasse der abstrakten Klasse Format und dient dazu, Nachrichten sprachunabhängig zu erzeugen. Das heißt, die tatsächliche sprachabhängige Ausgabe wird so weit wie möglich nach hinten geschoben und erst dann erzeugt, wenn die Nachricht dem Benutzer angezeigt werden soll. Durch MessageFormat werden nur Formatierungsanweisungen gegeben, und die wirklichen Informationen (also die Objekte als Informationsträger) werden zur Laufzeit eingesetzt. Dabei enthalten die Formatierungsanweisungen Platzhalter für diese Objekte. In der Regel werden Daten (die Argumente) erst zur Laufzeit ermittelt, wie etwa die Zeilennummer einer Fehlerstelle in einer Eingabedatei.

    Beispiel Eine Anwendung des Formatierers. Der format()-Befehl formatiert die Argumente, die in einem Objekt-Feld abgelegt sind, mit dem Aussehen, wie es im Konstruktor des MessageFormat-Objekts angegeben wurde.

    Object[] testArgs = { 31415L, "SchnelleLotte" };
    MessageFormat form = new MessageFormat(
      "Anzahl Dateien auf der Festplatte \"{1}\": {0}." );
    System.out.println( form.format(testArgs) );

    Die Ausgabe mit unterschiedlichen testArgs ist:

    Anzahl Dateien auf der Festplatte "SchnelleLotte": 0.
    Anzahl Dateien auf der Festplatte "SchnelleLotte": 1.
    Anzahl Dateien auf der Festplatte "SchnelleLotte": 31,415.

    Die Argumente aus dem Array werden über die Platzhalter wie {0} in die Nachricht eingefügt. Die Nummern entsprechen der Reihenfolge der Argumente im Array. Einträge im Array können ungenutzt bleiben. Fehlt allerdings das einem Platzhalter entsprechende Element im Feld, so wird eine ParseException ausgelöst.

    class java.text.MessageFormat extends Format

    • MessageFormat( String pattern ). Erzeugt ein MessageFormat-Objekt mit dem angegebenen Pattern.

    Gegenüber anderen Format-Klassen zeigt die Klasse MessageFormat eine Besonderheit beim Erzeugen: MessageFormat-Objekte werden über ihren Konstruktor erzeugt und nicht über getInstance(). Der Grund ist, dass üblicherweise die Erzeugungsfunktionen – damit sind die getInstance()-Varianten gemeint – eine komplexe Initialisierung durchlaufen, die die landesspezifischen Einstellungen festlegen. MessageFormat ist aber an keine bestimmte Sprache gebunden und benötigt folglich auch keine Initialisierung.

    Bildungsgesetz für Message-Formate

    Die Zeichenkette für MessageFormat enthält die Format-Elemente, die in geschweiften Klammern gesetzt sind. Steht dort nur der Index – wie {0} –, ist das der einfachste Fall. Die API-Dokumentation von MessageFormat zeigt jedoch, dass die Angaben auch präziser ausfallen können:

    • { ArgumentIndex, FormatType }: Element wird nach dem angegebenen Format-Typ number, date, time oder choice formatiert. MessageFormat besorgt sich zum Beispiel im Fall vom Format-Typ number über NumberFormat.getInstance(getLocale()) einen passenden Formatierer.
    • { ArgumentIndex, FormatType, FormatStyle }: Neben dem Format-Typ lässt sich der Stil festlegen. Vordefiniert sind short, medium, long, full, integer, currency und percent. Ein eigener Formatierungsstil lässt sich auch angeben, der aber zur Unterscheidung in einfachen Hochkommata eingeschlossen werden muss.

    Abschließend sei ein Beispiel mit MessageFormat gegeben, das das gleiche Argument unterschiedlich formatiert:

    Object[] arguments = {
      new Date(),
      "die Antwort auf alle Fragen",
      42 // Integer object
    };
    String result = MessageFormat.format(
      "Am {0,date} um {0,time} ist {1} wie immer {2,number,integer}.", arguments );
    System.out.println( result );

    Dies erzeugt die Ausgabe:

    Am 21.08.2007 um 15:43:56 ist die Antwort auf alle Fragen wie immer 42.

    Hinweis Bei den geschweiften Klammern besteht Verwechslungsgefahr zwischen Message-Platzhalter und normalem Zeichen. Das ist insbesondere ein Problem, wenn die Nachricht mit den Platzhaltern eine beliebige Datei ist (etwa ein Java-Programm, in dem der Name der Klasse durch einen Platzhalter angedeutet ist). Dann muss jede normale geschweifte Klammer { durch \'{‚ ersetzt werden.

    Alternative Sprachen für die JVM

    Die hochoptimierte JVM und die umfangreichen Java-Bibliotheken lassen sich mittlerweile durch alternative Programmiersprachen nutzen. Auf der einen Seite existieren klassische Interpreter und Compiler für existierende Sprachen, wie Ruby, Prolog, LISP, BASIC, Python, die bestmöglich auf die Java-Umgebung portiert werden. Auf der anderen Seite sind es ganz neue Programmiersprachen (wie schon das genannte Groovy), die sich als echte Alternative zur Programmiersprache Java etablieren. Skriptsprachen werden oft über die JSR 223: Scripting for the Java Platform, einer standardisierten API, angesprochen.

    Dieser Abschnitt gibt einen kleinen Überblick über aktuelle Programmiersprachen auf der JVM. Im ersten Teil geht es um existierende Programmiersprache, die auf die JVM gebracht werden.

    JavaScript

    Seit Java 6 ist eine JavaScript-Laufzeitumgebung integriert. Die Script-Engine erlaubt die dynamische Übersetzung in Bytecode, was schnelle Ausführungszeiten der funktionalen objektorientierten Programmiersprache garantiert.

    JRuby

    JRuby (http://jruby.org/) ist die Java-Version der dynamisch getypten Programmiersprache Ruby (http://www.ruby-lang.org/). Ruby wird mit einem Atemzug mit dem Web-Framework Ruby on Rails genannt, ein Framework für Web-Applikationen, welches dank JRuby auch auf jedem Tomcat und Java Application-Server läuft.

    Jython

    Die beliebte Programmiersprache Python (http://www.python.org/) bringt Jython (http://jython.org/) auf die Java-Plattform. Auch Jython übersetzt Python-Programme in Java-Bytecode und erlaubt relativ schnelle Ausführungszeiten. Jython 2.5 implementiert Python auf 2.5, doch hat sich (C-)Python mit Version 2.7 und 3.3 auch schon weiter entwickelt. Auch sonst gibt es Unterschiede, etwa bei den eingebauten (nativen) Funktionen.

    Quercus

    Quercus (http://quercus.caucho.com/) ist eine Implementierung der Programmiersprache PHP, entwickelt von Caucho Technology. Mit Quercus lasen sich viele beliebte PHP-Projekte in einer Java-Umgebung ausführen. In der Java-Welt werden zwar nicht alle PHP-Funktionen unterstützt, aber dafür gibt es in der Java-Welt keine Speicherüberläufe oder Sicherheitsprobleme.

    Clojure

    Die Programmiersprache Clojure (http://clojure.org/) ist ein LISP-Dialekt und fällt so in die Kategorie der funktionalen Programmiersprachen. Der Compiler erzeugt direkten Byte-Code. Für die Kommandozeile gibt es ein kleines Tool (Read-Eval-Print-Loop (REPL)), mit dem jeder erste Versuche von der Kommandozeile machen kann. Seit einiger Zeit gibt es auch Umsetzungen für .NET und JavaScript.

    LuaJava

    Eine Umsetzung der Programmiersprache Lua (http://www.lua.org/) für die JVM ist LuaJava (http://www.keplerproject.org/luajava/) bzw. Juaj (http://sourceforge.net/projects/luaj/). Die aus Brasilien stammende dynamisch getypte Programmiersprache Lua zählt zu den performantesten, interpretierten Skriptsprachen. Sie ist in erster Linie als eingebettete Programmiersprache zur Applikationssteuerung entworfen worden; prominente Nutzer sind Sim City, World of Worcraft, Adobe Photoshop Lightroom, SciTE, weiter unter http://www.lua.org/uses.html.

    JLog

    JLog (http://jlogic.sourceforge.net/) implementiert einen ISO-standardisierten PROLOG-Interpreter. JLog läuft in einem eigenen Fenster mit Quellcode-Editor, Query-Panel, Hilfe, Debugger, oder es kann in einem eigenen Java-Programm eingebettet werden.

    Die Wikipedia-Seite https://en.wikipedia.org/wiki/List_of_JVM_languages führt weitere Programmiersprachen für die JVM auf. Allerdings sind viele der gelisteten Sprachen für sehr spezielle Anwendungsfälle entworfen, experimentell oder werden nicht mehr gepflegt.

    Die genannten Implementierungen bringen eine bekannte Sprache auf die Java Umgebung, so dass zum Beispiel Code zwischen Plattformen ausgetauscht werden kann. Es gibt auch komplette Neuentwicklungen für neue Programmiersprachen.

    Groovy

    Groovy bietet eine starke Syntax mit Closures, Listen/Mengen, reguläre Ausdrücke, eine dynamische und statische Typisierung und vielem mehr. Moderne IDEs wie Eclipse oder NetBeans unterstützen Groovy durch Plugins (http://groovy.codehaus.org/Eclipse+Plugin, http://groovy.codehaus.org/NetBeans+Plugin). Der Groovy-Compiler erzeugt für die Groovy-Klassen den typischen Bytecode, sodass normale Java-Klassen problemlos Groovy-Klassen nutzen können – oder umgekehrt.

    Scala

    Scala ist eine funktionale, objektorientierte Programmiersprache, die in der Java-Community große Zustimmung findet. Plugins für diverse Entwicklungsumgebungen stehen ebenfalls bereit. Auch für die .NET-Plattform gibt es Implementierung. Besonders zeichnet Scala ein durchdachtes Typsystem aus. Für viele Entwickler ist es „Java 2.0“.

    In den letzten Jahren sind vermehrt neue JVM-Programmiersprachen aufgetaucht, sie sind vom Sprachdesign auf jeden Fall interessant, finden aber bisher kaum großen Einsatz. Zu diesen zählen etwa Fantom (http://fantom.org/), Ceylon (http://ceylon-lang.org/) oder Gosu (http://gosu-lang.org/).

    Geschichte: Als Java noch unter der Sonne stand, stellte Sun zentrale Entwickler ein, um die Weiterentwicklung von Skriptsprachen unter der JVM zu unterstützen. Darunter etwa im März 2008 Frank Wierzbicki, Hauptentwickler von Jython. Doch schon nach 1,5 Jahren verließ er Sun wieder.[1] Das gleiche Spiel mit den Entwicklern von JRuby, Charles Nutter und Thomas Enebo, die 2006 zu Sun gingen und 2009 das Unternehmen in der Oracle- Akquisationsphase verließen.[2] NetBeans bot den besten (J)Ruby-Editor, doch entfernte die Version NetBeans 7.0 die Unterstützung komplett.[3]


    [1] http://fwierzbicki.blogspot.de/2009/11/leaving-sun.html

    [2] http://news.idg.no/cw/art.cfm?id=C0D2078D-1A64-6A71-CE889FFB617BA47D

    [3] https://netbeans.org/features/ruby/index.html

    Bits rotieren

    Java hat zwar Operatoren zum Verschieben der Bit, aber bis Java 5 nicht zum Rotieren. Beim Rotieren werden einige Bit um eine bestimmte Stelle verschoben, doch die herausfallenden Bit kommen auf der anderen Seite wieder hinein.

    Eine Funktion ist leicht geschrieben: Der Trick dabei besteht darin, die herausfallenden Bit vorher zu extrahieren und auf der anderen Seite wieder
    einzusetzen.

    public static int rotateLeft( int v, int n )
    {
      return (v << n) | (v >>> (32 – n));
    }
    
    public static int rotateRight( int v, int n )
    {
      return (v >>> n) | (v << (32 – n));
    }

    Die Funktionen rotieren jeweils n Bit nach links oder rechts. Da der Datentyp int ist, ist die Verschiebung n in dem Wertebereich von 0 bis 31 sinnvoll.

    Seit Java 5 regeln die Funktionen wie Integer.rotateLeft(…) die Rotation.

    Die Ackermann-Funktion

    Die Ackermann-Funktion ist ein prominentes Beispiel für eine rekursive Funktion, die jetzt — und noch die nächsten Jahrzehnte — Informatiker im Studium beschäftigt.

    Sie ist nach F. Wilhelm Ackermann (1886-1962) benannt. Viele Funktionen der mathematischen Praxis sind primitiv rekursiv, und David Hilbert stellte 1926 die Frage, ob alle Funktionen, deren Argumente und Werte natürliche Zahlen sind, primitiv rekursiv sind. Die Ackermann-Funktion steigt sehr stark an und ist für Theoretiker ein Beispiel dafür, dass es berechenbare Funktionen gibt, die aber nicht primitiv rekursiv sind. Im Jahre 1928 zeigte Ackermann dies an einem Beispiel: der Ackermann-Funktion.6 Sie wächst stärker als es Substitution und Rekursion ermöglichen und nur für kleine Argumente lassen sich die Funktionswerte noch ohne Rekursion berechnen. Darin bestand auch die Beweisidee von Ackermann, eine Funktion zu definieren, die schneller wächst als alle primitiv rekursiven Funktionen. Wir wollen hier nicht die originale Version von Ackermann benutzen, die durch die Funktionalgleichung

    f(n‘, x‘, y‘) = f(n, f(n‘, x, y), x)

    ausgedrückt wird, sondern die vereinfachte Variante von Hans Hermes. Wir wollen die Version von Hermes aber fortan auch Ackermann-Funktion nennen, da sie direkt aus dem Original gewonnen wird. Für die oben angegebene Funktion muss in der Abhandlung von Ackermann nachgeblättert werden, um den Nachweis des Nicht-primitiv-rekursiv-Seins zu finden.

    Die neue Ackermann-Funktion ist eine Abbildung von zwei ganzen Zahlen auf eine ganze Zahl a(n,m). Sie ist mathematisch durch folgende Gesetzmäßigkeit definiert:

    a(0,m) = m + 1
    a(n,0) = a(n – 1, 1)
    a(n,m) = a(n – 1, a(n, m – 1))

    Die Ackermann-Funktion ist dafür berühmt, die Rechenkapazität ganz schnell zu erschöpfen. Sehen wir uns die Implementierung in Java an und testen wir das Programm mit ein paar Werten.

    class Ackermann
    {
      public static long ackermann( long n, long m )
      {
        if ( n == 0 )
          return m + 1;
        else
        
          if ( m == 0 )
            return ackermann( n - 1, 1 );
          else
            return ackermann( n - 1, ackermann(n, m - 1) );
        
      }
      public static void main( String args[] )
      {
        int x = 2,
            y = 2;
        System.out.println( "ackermann(" + x + "," + y + ")=" + ackermann(x,y) );
      }
    }

    Für den Aufruf ackermann(1, 2) veranschaulicht die folgende Ausgabe die rekursive Eigenschaft der Ackermann-Funktion. Die Stufen der Rekursion sind durch Einrückungen deutlich gemacht:

    a(1,2):
     a(0,a(1,1)):
      a(0,a(1,0)):
       a(1,0):
        a(0,1)=2
       a(1,0)=2
       a(0,2)=3
      a(0,a(1,0))=3
      a(0,3)=4
     a(0,a(1,1))=4
    a(1,2)=4

    Bei festen Zahlen lässt sich der Wert der Ackermann-Funktion direkt angeben.

    a(1,n) = n + 2

    a(2,n) = 2n + 3

    a(3,n) = 2n+3 – 3

    Für große Zahlen übersteigt die Funktion aber schnell alle Berechnungsmöglichkeiten. Zum Vergleich: Die Ackermannfunktion berechnet beim Zahlenpaar (4,7) etwa 0,2*10^20. Unter der Definition a(0,y) = y+1; a(x+1) = a(x,1); a(x+1,y+1) = a(x, (a+1),y)) ergibt sie die folgende große Tabelle.

    A(x,y)

    y = 0

    y = 1

    y = 2

    y = 3

    y = 4

    y = 5

    x = 0

    1

    2

    3

    4

    5

    6

    x = 1

    2

    3

    4

    5

    6

    7

    x = 2

    3

    5

    7

    9

    11

    13

    x = 3

    5

    13

    29

    61

    125

    253

    x = 4

    13

    65533

    265536-3

    2265536-3

    a(3,

    2265536-3)

    a(3,a(4,4))

    x = 5

    65533

    a(4,65533)

    a(4,

    a(4,65533))

    a(4,a(5,2))

    a(4,a(5,3))

    a(4,a(5,4))

    x = 6

    a(4,65533)

    a(5,

    a(4,65533))

    a(5,a(6,1))

    a(5,a(6,2))

    a(5,a(6,3))

    a(5,a(6,4))

    Ackermann-Funktion für einige Werte

    Die Ackermann Funktion wächst, obwohl sie Turing-berechenbar ist, schneller als alle primitiv rekursiven Funktionen. Normale mathematische Notationen versagen beim Schreiben dieser großen Zahlen und eine andere Möglichkeit muss her um große Zahlen zu definieren. Schauen wir uns doch erst einmal an, woher die großen Zahlen überhaupt kommen. Dazu eine alternative Definition, nach der ersten Zahle entwickelt:

    a(0,n) = n + 1

    a(1,n) = 2 + (n + 3) – 3

    a(2,n) = 2 * (n + 3) – 3

    a(3,n) = 2 ^ (n + 3) – 3

    a(4,n) = 2 ^ 2 ^ … ^ 2 – 3 (wobei die Potenz (n+3)-mal erhoben wird)

    Schon bei der Definition von a(4,n) tritt n mal eine Zweierpotenz auf. Mit der herkömmlichen Schreibweise unhandelbar. Glücklicherweise haben sich berühmte Männer mit neuen Notationen einen Namen gemacht, unter ihnen der nimmer müde werdende Donald E. Knuth. Er schuf 1976 die Up-Arrow-Notation (auf deutsch etwa Nach-oben-Pfeil-Notation – wir bleiben an dieser Stelle aber englisch). Die Schreibweise wird am besten an der Motivation, die Multiplikation einzuführen, verdeutlicht. Addieren wir n mal den Summanden m, so entspricht dies einer Multiplikation vom m mit der Anzahl n (m + m + … + m = m * n). Bilden wir die Potenz einer Zahl, multiplieren wir n mal m, so schreibt sich dies m ^ n = m m … m = mn. Führen wir dies weiter. Was kommt mach dem n-maligen Produkt? Es kommt danach die n-te Potenz (in Zeichen m^^n, daher zwei mal das Zeichen ^^). Fassen wir bisheriges mit einigen Beispielen zusammen:

  • m n = m + m + … + m = m * n
  • m^n = m m … m = mn

    2^2 = 2*2 = 4

    2^3 = 2*2*2 = 8

    2^4 = 2*2*2*2 = 16

    3^2 = 3*3 = 9

    3^3 = 3*3*3 = 27

    3^4 = 3*3*3*3 = 81

    4^2 = 4*4 = 16

    4^3 = 4*4*4 = 64

    4^4 = 4*4*4*4 = 256

  • m ^^ n = m ^ m ^ … ^ m = mm…m

    2^^2 = 2^2 = 4

    2^^3 = 2^2^2 = 2^4 = 16

    2^^4 = 2^2^2^2 = 2^16 = 65536

    3^^2 = 3^3 = 27

    3^^3 = 3^3^3 = 3^27 = 7625597484987

    3^^4 = 3^3^3^3 = 3^3^27 = 3^7625597484987

  • m ^^^ n = m ^^ m ^^ … ^^ m

    2^^^2 = 2^^2 = 4

    2^^^3 = 2^^2^^2 = 2^^4 = 65536

    2^^^4 = 2^^2^^2^^2 = 2^^65536 = 2^2^…^2 (65536 mal)

    3^^^2 = 3^^3 = 7625597484987

    3^^^3 = 3^^3^^3 = 3^^7625597484987 = 3^3^…^3 (7625597484987 mal)

    3^^^4 = 3^^3^^3^^3 = 3^^3^^7625597484987 = 3^3^…^3 (3^^7625597484987 mal)

  • m ^^^^ n = m ^^^ m ^^^ … ^^^ m (n mal)

    2^^^^2 = 2^^^2 = 4

    2^^^^3 = 2^^^2^^^2 = 2^^^4 = 2^2^…^2 (65536 mal)

    2^^^^4 = 2^^^2^^^2^^^2 = 2^^^2^2^…^2 (65536 mal)

    3^^^^2 = 3^^^3 = 3^3^…^3 (7625597484987 mal)

    3^^^^3 = 3^^^3^^^3 = 3^^^3^3^…^3 (7625597484987mal)

    = 3^^3^3^…^3 (3^3^…^3 (7625597484987 mal) mal)

  • Nun endlich lassen sich die großen Zahlen mit kleinen Formeln schreiben. So für die ersten fünf Elemente:

    a(1, n) = 2 + (n + 3) – 3

    a(2, n) = 2 (n + 3) – 3

    a(3, n) = 2 ^ (n + 3) – 3

    a(4, n) = 2 ^^ (n + 3) – 3

    a(5, n) = 2 ^^^ (n + 3) – 3

    John H. Conway geht noch einen Schritt weiter, denn auch die Up-Arrow-Schreibweise kann die Formel für a(m,n) nicht ohne den Zusatz »^…^ und dass n-2 mal« lösen. Conway schafft dies mit seiner Chained-Arrow Notation (zu deutsch etwa Ketten-Pfeil Notation, aber wir bleiben wieder beim englischen Begriff). Conway führt für »^…^« die Schreiweise

    a -> b -> c = a ^^…^^ b

    ein, wobei c die Anzahl der Potenzen beschreibt. Nun endlich vereinfacht sich auch der die Ackermann-Funktion. Es folgt:

    a(m, n) = 2 -> (n + 3) -> (m – 2) – 3

    Felder sind implizit Serializable

    Primitive Datentypen werden beim Serialisierungs-Prozess selbst in den Datenstrom geschrieben. Das gleiche gilt auch für Felder; sie sind automatisch Serializable.

    Neben der Methode clone() und dem Attribut length besitzt ein Feld eine zweite wichtige Eigenschaft, die eng mit clone() verbunden ist: Ein Feld lässt sich serialisieren. Dazu muss aber ein Array-Objekt die Schnittstelle java.io.Serializable implementieren, und dies macht es auch versteckt.

    Betrachten wir das folgende Programm, so erkennen wir, dass nur bei einer gültigen Referenz auf ein Feld-Objekt dieses Objekt instanceof Serializable ist.

    class ArrayIsSerializable
    {
      public static void main( String args[] )
      {
        int f1[] = null;
        int f2[] = new int[10];
    
        Serializable s = (Serializable)f1;
    
        System.out.println( s );   // null
    
        boolean b1 = f1 instanceof Serializable;
        boolean b2 = f2 instanceof Serializable;
    
        System.out.println( b1 );  // false
        System.out.println( b2 );  // true
      }
    }

    Die abstrakten Basisklassen für Container

    Das Designprinzip der Collection-Klassen folgt drei Stufen:

    • Schnittstellen legen Gruppen von Operationen für die verschiedenen Behältertypen fest.
    • Abstrakte Basisklassen führen die Operationen der Schnittstellen auf eine minimale Zahl von als abstrakt deklarierten Grundoperationen zurück, etwa addAll() auf add() oder isEmpty() auf getSize().
    • Konkrete Klassen für bestimmte Behältertypen beerben die entsprechende abstrakte Basisklasse und ergänzen die unbedingt erforderlichen Grundoperationen (und einige die Performance steigernde Abkürzungen gegenüber der allgemeinen Lösung in der Oberklasse).

    Es gibt eine Reihe von abstrakten Basisklassen, die den Containern eine Basisfunktionalität geben. Unter ihnen sind:

    AbstractCollection

    Implementiert die Methoden der Schnittstelle Collection ohne iterator() und size(). AbstractCollection ist Basisklasse von AbstractList und AbstractSet.

    AbstractList

    Erweitert AbstractCollection und implementiert die Schnittstelle List. Für eine konkrete Klasse müssen lediglich Methoden für get(int index) und size() implementiert werden. Soll die Liste auch Elemente aufnehmen, sollte sie auch set(int index, Object element) implementieren. Andernfalls bewirkt das Einfügen von Elementen nur eine Unsupported OperationException. Die direkten Unterklassen sind AbstractSequentialList, ArrayList und Vector.

    AbstractSequentialList

    AbstractSequentialList erweitert AbstractList (und damit auch AbstractCollection) und bildet die Grundlage für die Klasse LinkedList. Im Gegensatz zur konkreten Klasse ArrayList bereitet AbstractSequentialList die Klasse LinkedList darauf vor, die Elemente in einer Liste zu verwalten und nicht wie ArrayList in einem internen Array.

    AbstractSet

    Erweitert AbstractCollection und implementiert die Schnittstelle Set. Die Klasse AbstractSet dient als Basis für die beiden Klassen HashSet und TreeSet. Es überschreibt auch keine Methoden der Oberklasse AbstractCollection, sondern fügt nur die Methode equals(Object) und hashCode() hinzu.

    AbstractMap

    Implementiert die Schnittstelle Map. Um eine konkrete Unterklasse zu erstellen, muss put() sinnvoll implementiert werden; ohne Überschriebenes put() folgt eine UnsupportedOperationException. Für get(Object) gibt es eine Standard-Implementierung.

    Das Wissen um diese Basisimplementierung ist nützlich, wenn eigene Datenstrukturen implementiert werden. Das passiert selten, findet man aber etwa bei Google Guava.

    Absolute Koordinaten einer Gui-Komponente

    Um die absoluten Koordinaten des Elements am Bildschirm zu bestimmen, können wir folgende Idee einsetzen: Zunächst bestimmen wir die Koordinaten des Elements relativ zum Fenster. Sie sollen in den Variablen x und y stehen. Dann hangeln wir uns von der aktuellen Komponente bis zum aktuellen Fenster durch und fragen das Frame-Objekt über getLocation() nach der absoluten Position. Die Fenster-Koordinaten addieren wir zu den relativen Komponenten-Koordinaten, um die absoluten Koordinaten zu erhalten.

    Component c = this;
    while ( (c = c.getParent()) != null )
    if ( c instanceof JFrame )
    break;
    if ( c != null )
    {
      Point p = ((JFrame) c).getLocation();
      x += p.x;
      y += p.y;
    }

    Die Methode SwingUtilities.getWindowAncestor(Component) läuft für eine gegebene Komponente selbständig hoch und liefert das Window-Objekt oder null. Um einem Fenster ein Ereignis zum Schließen zu schicken, schreiben wir einfach:

    Window w = SwingUtilities.getWindowAncestor( c );
    w.dispatchEvent( new WindowEvent(w, WindowEvent.WINDOW_CLOSING) );

    Java EE-Beispiele von Apache TomEE

    Siehe https://tomee.apache.org/examples-trunk/index.html:

    Session Beans
    EntityManagers
  • injection-of-entitymanager *
  • jpa-eclipselink
  • jpa-hibernate
  • jpa-enumerated *
  • CDI
    EJB
  • access-timeout *
  • async-methods *
  • schedule-expression *
  • schedule-methods
  • interceptors
  • async-postconstruct
  • REST
  • simple-rest
  • rest-example
  • rest-example-with-application
  • rest-on-ejb
  • rest-xml-json
  • Web Services
  • simple-webservice *
  • webservice-handlerchain *
  • webservice-holder *
  • webservice-attachments
  • webservice-inheritance
  • webservice-security
  • webservice-ws-security
  • JMS and MDBs
  • injection-of-connectionfactory
  • simple-mdb-with-descriptor
  • simple-mdb *
  • Transactions
  • testing-transactions
  • transaction-rollback
  • applicationexception
  • Security
  • testing-security-3
  • testing-security-2
  • testing-security
  • DataSources
  • injection-of-datasource
  • datasource-ciphered-password *
  • dynamic-datasource-routing *
  • resources-declared-in-webapp
  • Referencing EJBs
  • injection-of-ejbs *
  • lookup-of-ejbs-with-descriptor
  • lookup-of-ejbs
  • Environment Entries
  • injection-of-env-entry
  • custom-injection
  • Java EE Connectors
  • quartz-app *
  • Testing Techniques
  • alternate-descriptors *
  • application-composer *
  • testcase-injection
  • ear-testing *
  • Frameworks
  • spring-integration
  • spring-data-proxy
  • struts
  • Meta-Annotations
  • access-timeout-meta *
  • schedule-methods-meta
  • testing-security-meta
  • movies-complete-meta
  • movies-complete
  • Proxy Beans
  • dynamic-dao-implementation *
  • dynamic-implementation
  • dynamic-proxy-to-access-mbean
  • spring-data-proxy
  • EJB Legacy
    Other Features
  • mbean-auto-registration *
  • bean-validation-design-by-contract *
  • telephone-stateful *
  • troubleshooting
  • Misc
  • applet
  • ejb-examples
  • ejb-webservice
  • jsf-cdi-and-ejb
  • jsf-managedBean-and-ejb
  • moviefun
  • helloworld-weblogic
  • polling-parent
  • Google Annotations Gallery: Immer wieder ein Schmunzler

    Siehe https://code.google.com/p/gag/:

    Disclaimer

    • @AhaMoment
    • @BossMadeMeDoIt
    • @HandsOff
    • @IAmAwesome
    • @LegacySucks

    Enforceable

    • @CantTouchThis
    • @ImaLetYouFinishBut

    Literary Verse (new subcategory)

    • @Burma Shave
    • @Clerihew
    • @DoubleDactyl
    • @Haiku (moved to this subcategory)
    • @Limerick
    • @Sonnet

    Remarks

    • @Fail
    • @OhNoYouDidnt
    • @RTFM
    • @Win

    Team (new category)

    • @Blame
    • @Channeling
    • @Fired
    • @HonorableMention
    • @Visionary