dckg.net
Blog
Tools
Downloads
Dienstag, 31 Januar 2017 in Programmierung
Java: Struct und StructService
Ich habe es getan. Struct in Java integriert.

Seitdem ich begann, mich mit Java zu beschäftigen, habe ich struct aus C/C++ vermisst.
Simple, effiziente Datenstrukturen.

Doch halt - wir sind hier bei Java - es gibt bereits alles in den Standardbibliotheken, wenn nicht dort, lässt sich bestimmt eine Lib finden, die es kann. Die getestet ist.
Die von milliardenschweren Unternehmen eingesetzt und weiterentwickelt werden.
Und nochmal: Die getestet ist.

Wozu noch selbst programmieren?
Im Wesentlichen gibt es drei Gründe:

  • Lizenzen mit eigenem Projekt inkompatibel (z.B. GPL)
  • Spezialanforderungen
  • Forschung und Lehre

Ein prominentes Beispiel für eine Serialiserungsbibliothek: Protocol Buffers  |  Google Developers
Super effizient das Ganze, Protocol Buffer Definitionen ("Messages") müssen mit einem Compiler präkompiliert werden, daher rührt die hohe Geschwindigkeit - die Serialiserungslogik wird nicht etwa wie die standard Java Serialiserung zur Laufzeit mittels Reflection ausgeführt, bei Google liegt die Logik zur Kompilierungszeit statisch vor.

Meine Anforderungen sind

  • Serialiserter Output nicht größer als eine C-Struct, nur reine Daten
  • Java Reflection hinter mich bringen

Nehmen wir diese Klasse als Beispiel:

public final class MyStruct implements Serializable {
    public int x;
    public int y;
}

Wollte ich diese Klasse serialisieren, um sie bswp. in einer Datei zu speichern oder über das Netzwerk zu verschicken, kein Problem mit Standard Java Serialisierung.

ObjectOutputStream (Java Platform SE 8 )

Serializable implementieren, und das Objekt der writeObject Methode übergeben. Java macht alles automatisch, wie man es gewohnt ist. Mit dem transient Schlüsselwort könnte man Instanzvariablen von der Serialiserung ausnehmen. Was es in C schon mal nicht gibt.

Soweit, so gut. Leider braucht das Ergebnis mehr als ein vielfaches von 8 Bytes, was den 2 int an Daten in der Klasse entspricht.
Java schreibt alles rein. Klassenname, Namen der Membervariablen, ...

Das muss nicht sein. Möchte man selbst bestimmen, wie serialisert wird - ganz einfach Externalizable implementieren.

An dieser Stelle gibt es jetzt zwei Probleme: Ich möchte nicht in jeder "Struct" manuell Funktionen zur Serialisierung schreiben, des Weiteren braucht die mit Externalizable serialisierte Klasse weniger Speicherplatz als Serializable, gleichwohl sind die produzierten Daten größer als 8 Byte.

Da schaue ich mir kurzerhand den Source an und werde schließlich in den ObjectStreamConstants fündig. Konstanten, die vor/zwischen/am Ende meiner Daten gesetzt werden, unter anderem der 4 Byte große Header.

Struct

Github: dckg/struct: Java serialization library. Missing struct from C/C++? This is for you!

Ich möchte nicht alles nochmal auf deutsch schreiben, daher hier die Kurzfassung:
Eine Klasse hat bloß public Attribute, diese sind primitve Wrapperklassen (z.B. Integer).
Struct holt sich via Reflection eine Liste der Attribute, sortiert diese nach Namen und schreibt sie auf einem DataOutputStream runter. Selbes Spiel beim Lesen, in umgekehrter Reihenfolge, versteht sich.

Ob jetzt zwei verschiedene Programme die selbe Struct Definition haben, korrupt sind etc.
Es gibt keinerlei Metainformationen, nichts. Bleibt alles dem User überlassen.

Zugegeben, es ist für den Java-Programmierer etwas befremdlich, eine Klasse ohne Getter-/Setter-Ballast, sowie ausschließlich public Membern zu sehen - jedoch völlig legitim:

One example of appropriate public instance variables is the case where the class is essentially a
data structure, with no behavior. In other words, if you would have used a struct instead of a
class (if Java supported struct), then it’s appropriate to make the class’s instance variables
public.
Java Code Conventions - Oracle

In Java dann allein mit Primitiven arbeiten ist auch nicht, darum wird alles andere von Java's interner Serialiserung als byte-Array serialisert.

Struct Service - RPC/RMI

Indem ich verschiedene Struct Typen in einem Enum festlege und anhand dessen verschiedene Callback Methoden aufrufen lasse, die die entsprechende Struct verarbeiten.
Enum zur Laufzeit deklarieren, geht nicht, gibt's nicht.

UML Diagramm, dass das Zusammenspiel zwischen Struct und StructService illustriert.
uml relations of struct, structservice

Die Reflection Performance ist besser als von mir erwartet, setAccesible(true) sowie Caching der Methoden tragen maßgeblich zur Performance meiner Bibliothek bei.

Single Thread Performance, 3.4 Ghz (Intel Xeon E3-1231v3), Struct erstellen, schreiben, lesen, vergleichen.

Struct Speed
8 Byte Struct wie oben > 1.000.000 Struct/sec.
80 Byte Struct > 130.000 Struct/sec

Siehe improved performance · dckg/struct@2381b67

Site optimised for Google Chrome / Chromium