1. 序章

このチュートリアルでは、FastUtilライブラリについて説明します。

まず、タイプ固有のコレクションの例をいくつかコーディングします。

次に、FastUtilに名前を付けるパフォーマンスを分析します。

最後に、FastUtilのBigArrayユーティリティを見てみましょう。

2. 特徴

FastUtil Javaライブラリは、Javaコレクションフレームワークを拡張しようとしています。 タイプ固有のマップ、セット、リスト、およびキューを提供し、メモリフットプリントが小さく、アクセスと挿入が高速です。 FastUtil は、大きな(64ビット)配列、セット、およびリストを操作および操作するためのユーティリティのセットも提供します。

ライブラリには、バイナリファイルとテキストファイル用の多数の実用的な入出力クラスも含まれています。

その最新リリースであるFastUtil8、は、JDKの機能インターフェイスを拡張するタイプ固有の関数のホストもリリースしました。

2.1. スピード

多くの場合、FastUtilの実装は利用可能な最速です。 著者は、独自の詳細を提供していますベンチマークレポート 、類似のライブラリと比較すると、 HPPCトローヴ。

このチュートリアルでは、 Javaマイクロベンチハーネス(JMH)を使用して独自のベンチマークを定義します。

3. フルサイズの依存関係

このチュートリアルでは、通常の JUnit 依存関係に加えて、FastUtilsおよびJMH依存関係を使用します。

pom.xmlファイルには次の依存関係が必要です。

<dependency>
    <groupId>it.unimi.dsi</groupId>
    <artifactId>fastutil</artifactId>
    <version>8.2.2</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.35</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.35</version>
    <scope>test</scope>
</dependency>

またはGradleユーザーの場合:

testCompile group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.19'
testCompile group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.19'
compile group: 'it.unimi.dsi', name: 'fastutil', version: '8.2.2'

3.1. カスタマイズされたjarファイル

ジェネリックスがないため、FastUtilsは多数のタイプ固有のクラスを生成します。 そして残念ながら、これは巨大なjarファイルにつながります。

ただし、幸いなことに、 FastUtils にはfind-deps.shスクリプトが含まれており、アプリケーションで使用するクラスのみで構成される、より小さく、より焦点を絞ったjarを生成できます。

4. タイプ固有のコレクション

始める前に、タイプ固有のコレクションをインスタンス化する簡単なプロセスを簡単に見てみましょう。 選びましょう HashMap を使用してキーと値を格納しますダブルス。 

この目的のために、FastUtilsDouble2DoubleMapインターフェースとDouble2DoubleOpenHashMap実装を提供します。

Double2DoubleMap d2dMap = new Double2DoubleOpenHashMap();

クラスをインスタンス化したので、JavaコレクションAPIのマップを使用する場合と同じように、データを簡単に入力できます。

d2dMap.put(2.0, 5.5);
d2dMap.put(3.0, 6.6);

最後に、データが正しく追加されたことを確認できます。

assertEquals(5.5, d2dMap.get(2.0));

4.1. パフォーマンス

FastUtilsは、パフォーマンスの高い実装に重点を置いています。 このセクションでは、JMHを使用してその事実を確認します。 Javaコレクションを比較してみましょう HashSet に対する実装 FastUtilの IntOpenHashSet

まず、 IntOpenHashSet:を実装する方法を見てみましょう。

@Param({"100", "1000", "10000", "100000"})
public int setSize;

@Benchmark
public IntSet givenFastUtilsIntSetWithInitialSizeSet_whenPopulated_checkTimeTaken() {
    IntSet intSet = new IntOpenHashSet(setSize);
    for(int i = 0; i < setSize; i++) {
        intSet.add(i);
    }
    return intSet; 
}

上記では、IntSetインターフェースのIntOpenHashSet実装を宣言しただけです。 また、初期サイズsetSize@Paramアノテーションで宣言しました。

簡単に言えば、これらの数値はJMHに入力され、さまざまなセットサイズの一連のベンチマークテストが生成されます。

次に、 Javaコレクションの実装を使用して同じことを行いましょう:

@Benchmark
public Set<Integer> givenCollectionsHashSetWithInitialSizeSet_whenPopulated_checkTimeTaken() {
    Set<Integer> intSet = new HashSet<>(setSize);
    for(int i = 0; i < setSize; i++) {
        intSet.add(i);
    }
    return intSet;
}

最後に、ベンチマークを実行して、2つの実装を比較してみましょう。

Benchmark                                     (setSize)  Mode  Cnt     Score   Units
givenCollectionsHashSetWithInitialSizeSet...        100  avgt    2     1.460   us/op
givenCollectionsHashSetWithInitialSizeSet...       1000  avgt    2    12.740   us/op
givenCollectionsHashSetWithInitialSizeSet...      10000  avgt    2   109.803   us/op
givenCollectionsHashSetWithInitialSizeSet...     100000  avgt    2  1870.696   us/op
givenFastUtilsIntSetWithInitialSizeSet...           100  avgt    2     0.369   us/op
givenFastUtilsIntSetWithInitialSizeSet...          1000  avgt    2     2.351   us/op
givenFastUtilsIntSetWithInitialSizeSet...         10000  avgt    2    37.789   us/op
givenFastUtilsIntSetWithInitialSizeSet...        100000  avgt    2   896.467   us/op

これらの結果は、FastUtilsの実装がJavaコレクションの代替案よりもはるかにパフォーマンスが高いことを明らかにしています。

5. ビッグコレクション

FastUtilsのもう1つの重要な機能は、64ビット配列を使用する機能です。 Javaの配列は、デフォルトで32ビットに制限されています。

はじめに、整数タイプのBigArraysクラスを見てみましょう。 IntBigArraysは、2次元整数配列を操作するための静的メソッドを提供します。これらの提供されたメソッドを使用することにより、基本的に、配列をよりユーザーフレンドリーな1次元配列にラップできます。

これがどのように機能するかを見てみましょう。

まず、1次元配列を初期化し、IntBigArrayのラップメソッドを使用して2次元配列に変換することから始めます。

int[] oneDArray = new int[] { 2, 1, 5, 2, 1, 7 };
int[][] twoDArray = IntBigArrays.wrap(oneDArray.clone());

必ずcloneメソッドを使用して、配列のディープコピーを確保する必要があります。

これで、リストまたはマップの場合と同様に、getメソッドを使用して要素にアクセスできます。

int firstIndex = IntBigArrays.get(twoDArray, 0);
int lastIndex = IntBigArrays.get(twoDArray, IntBigArrays.length(twoDArray)-1);

最後に、IntBigArrayが正しい値を返すことを確認するためのチェックを追加しましょう。

assertEquals(2, firstIndex);
assertEquals(7, lastIndex);

6. 結論

この記事では、FastUtilsのコア機能について詳しく説明しました。

いくつかのBigCollectionを試す前に、FastUtilが提供するタイプ固有のコレクションのいくつかを調べました。

いつものように、コードはGitHubにあります。