Koloboke für schnelle Java Collections

Die normale Java-Collections API kann die meisten normalen Use-Cases wunderbar abdecken, und ist im Allgemeinen sehr gut. Leider gibt es jedoch immer wieder Sonderfälle, in denen Workarounds oder inperformante Ansätze genommen werden müssen. Koloboke ist eine der Bibliotheken, die sich genau dieses Performance-Thema zum erklärten Ziel gemacht hat.

Wie funktioniert Koloboke?

Koloboke selbst ist ein Annotations-Prozessor, welcher eine stark optimierte Implementierung für spezifische Probleme generieren kann und hierbei auf Wunsch API-kompatibel mit den Standard-Interfaces wie Map, Set, List usw. bleibt.

Um Koloboke zu benutzen, müssen wir dies zuerst zu unseren Build hinzufügen, z. B. für Gradle wie folgt. Der integrierte Annotations-Prozessor wird dann entsprechend
beim Build ausgeführt:

dependencies {     
provided 'com.koloboke:koloboke-compile:0.5.1'
compile 'com.koloboke:koloboke-impl-common-jdk8:1.0.0'
}

Wie sieht nun ein einfaches Beispiel aus, um Koloboke zu benutzen? Zuerst erstellen wir eine abstrakte Klasse, die von der API her wie eine Map aussieht:

import com.koloboke.compile.KolobokeMap;
import java.util.Map;

@KolobokeMap
abstract class MyMap implements Map<String, Integer> {
    static MyMap withExpectedSize(int expectedSize) {
        return new KolobokeMyMap<String, Integer>(expectedSize);
    }
}

Koloboke wird uns die Klasse Koloboke+Name der abstrakten Klasse erstellen. Um diese vernünftig im Projekt zu benutzen, ist es empfehlenswert in die abstrakte Klasse gleich eine Factory mit einzubauen. In diese Falle die withExpectedSize-Methode. Dies ermöglicht es, alle internen Koloboke Spezifika vor dem User zu verbergen. Koloboke ermöglicht alternativ auch, statt einer abstrakten Klasse ein Interface zu benutzen.

Wichtig hierbei ist es, dass idealerweise eine möglichst exakte Typisierung vorgenommen wird, gerade bei primitiven Datentypen kann Koloboke dann optimierte Datenstrukturen verwenden. Weitere Optimierungen sind möglich, wenn nicht die Standardinterfaces verwendet werden, da z. B. eine Map ohne remove-Funktionalität einige Logik beim Einfügen weglassen kann.

Koloboke erlaubt eine erhebliche Flexibilität, die mit den normalen Collections nicht abbildbar ist. Beispielsweise:

@CustomKeyEquivalence ermöglicht es, eine eigene Implementierung für den equals– und hashCode-Check vorzugeben. Z. B. könnte damit der Key “1” identisch zu 1L behandelt werden.
@NullKeyAllowed generiert die Implementierung mit Unterstützung für null als Key.

@KolobokeMap
public abstract class OptimizedMap<K> {
    abstract void justPut(K key, int value);

    abstract int getInt(K key);

    abstract boolean justRemove(K key);
}

Die Methoden justGet und justRemove sind etwas schneller dadurch, dass sie keinen Rückgabewert haben. Die Methoden, die direkt primitive Typen zurückgeben (in diesem Beispiel getInt) verhindern das Autoboxing von Java und somit Objekterzeugungen.

@ConcurrentModificationUnchecked deaktiviert die ConcurrentModificationException-Logik, zugunsten der Performance.

Viele weitere Beispiele dazu, was mit Koloboke möglich ist, können auf der Projektseite angeschaut werden.

Short URL for this post: https://wp.me/p4nxik-3mU
This entry was posted in Did you know? and tagged . Bookmark the permalink.

Leave a Reply