標準のJavaライブラリには、int、longなど
のプリミティブ型の
コレクションを処理する機能がありません。 標準的な方法は、Integer、Longなどのクラスのオブジェクトを使用することです。
このアプローチは少数の要素でうまく機能します。これは、最初にすべての操作中に
オートボクシング/オートボックス解除が発生し、次にヒープ内のオブジェクトへの参照がコレクションに格納されるためです。 ヒープ内のオブジェクトは、メモリからの
オーバーヘッドを増やすだけでなく、
GCに負荷をかけます。
ヒープ内のオブジェクトには明らかなマイナスがもう1つあります。最新のプロセッサでのキャッシュです。 プロセッサは、ブロック単位でキャッシュにデータをロードします。 配列の順次処理の場合、いくつかの要素が一度にキャッシュにロードされます。 ヒープ上に散在するオブジェクトの場合、キャッシュ内のヒットは少なくなります。 キャッシュとメモリ階層の詳細については、
こちらをご覧ください 。
Troveライブラリは、プリミティブ型を操作するための標準コレクションインターフェイスを提供します。 ほとんどのアプリケーションでは、Troveコレクションの方が高速であり、メモリの消費も少なくなります。
コレクションセットには以下が含まれます。
jdkとは異なり、Trove Hashは
Open addressing collision resolutionメソッドを使用します。
命名原則はT <Type> <CollectionType>です。
例:
TIntListインターフェイス-intのリスト、TIntArrayListの実装:
TIntList l = new TIntArrayList();
TLongLongMap-長いキーと長い値を持つマップインターフェイス、TLongLongHashMapの実装:
TLongLongMap m = new TLongLongHashMap();
jdkコレクションでは、アイテムが見つからない場合、nullが返されます。 「NoEntryValue」はTroveに戻ります。デフォルトは0です。コレクションの作成時に
NoEntryValueを学習し、
NoEntryValueを設定
できます。
Troveコレクションの利点は処理方法です-forEach、
public static long troveEach() { final long [] rvalue = {0};
grep 、
inverseGrep-条件付きリストの
コンパイル (TList ...)および
transformValues-コレクション要素の
インプレース変更操作。
便利な機能-オブジェクト(オブジェクト継承)をキーとして持つHashMap / HashSetの場合、ハッシュ関数
Interface HashingStrategy <T>を使用できます。
ベンチマークには、優れたベンチマークツール
jmhを使用しました。
開発者がそれをMavenリポジトリに公開してくれたら素晴らしいと思います。書式設定を維持するために、出力をわずかに編集する必要がありました。1つの操作-100
万要素(
完全なレポートはこちら ):
$ java -version java version "1.7.0_21" Java(TM) SE Runtime Environment (build 1.7.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode) $ java -server -XX:+AggressiveOpts -Xms2048m \ -Xmx2048m -jar microbenchmarks.jar ".*Trove.*" -prof gc -i 3 -r 5s
ベンチマークモードThr Cnt Sec Mean Mean error Units
//リストに挿入<整数>
IntListJdkInsert thrpt 1 3 5 104.950 6.756 ops /秒
//リストの列挙を完了<整数>
IntListJdkTraverse thrpt 1 3 5 774.100 71.809 ops /秒
// TIntArrayListに挿入します
IntListTroveInsert thrpt 1 3 5 424.556 28.239 ops /秒
//完全な列挙TIntArrayList
IntListTroveTraverse thrpt 1 3 5 3548.806 7.712 ops /秒
// HashMapに挿入<Long、Long>
LongHashMapJdkInsert thrpt 1 3 5 24.683 1.994 ops /秒
// HashMap <Long、Long>内のすべての要素を順番に検索します
LongHashMapJdkSearch thrpt 1 3 5 67.789 1.119 ops /秒
//値の徹底的な検索HashMap <Long、Long>
LongHashMapJdkTraverse thrpt 1 3 5 99.761 0.882 ops /秒
// TLongLongMapに挿入
LongHashMapTroveInsert thrpt 1 3 5 28.750 0.165 ops /秒
// TLongLongMapのすべての要素を順番に検索します
LongHashMapTroveSearch thrpt 1 3 5 145.933 0.416 ops /秒
// forEachを使用したTLongLongMap値の完全な列挙
LongHashMapTroveTraverse thrpt 1 3 5 318.528 0.980 ops /秒
占有メモリの量を計算するのはそれほど簡単ではありませんが、GCアクティビティから間接的な結論を引き出すことができます。
jd List<Integer>: Iteration 1 (5s in 1 thread): 103,950 ops/sec GC | wall time = 5,002 secs, GC time = 0,331 secs, GC% = 6,62%, GC count = +24 Trove TIntArrayList: Iteration 1 (5s in 1 thread): 428,400 ops/sec GC | wall time = 5,002 secs, GC time = 0,019 secs, GC% = 0,38%, GC count = +32
TIntArrayListのソースを見ると、パフォーマンスの向上がどこから得られるかが明らかになります。データは配列として保存されます。
public class TIntArrayList implements TIntList, Externalizable { static final long serialVersionUID = 1L; protected int[] _data;
TLongLongMapの検索速度はforEachを使用して説明されます-一時オブジェクトは作成されず、ボックス化解除は除外されます。
同じベンチマークですが、1つの操作-1000の要素:
ベンチマークモードThr Cnt Sec Mean Mean error Units
IntListJdkInsert thrpt 1 3 5 239478.011 871.469 ops /秒
IntListJdkTraverse thrpt 1 3 5 1326701.717 1649.389 ops /秒
IntListTroveInsert thrpt 1 3 5 315562.594 2483.415 ops /秒
IntListTroveTraverse thrpt 1 3 5 3630599.806 10822.903 ops /秒
LongHashMapJdkInsert thrpt 1 3 5 45315.689 47.630 ops /秒
LongHashMapJdkSearch thrpt 1 3 5 114759.789 424.996 ops /秒
LongHashMapJdkTraverse thrpt 1 3 5 210012.550 139.001 ops /秒
LongHashMapTroveInsert thrpt 1 3 5 33078.583 119.127 ops /秒
LongHashMapTroveSearch thrpt 1 3 5 148311.567 267.613 ops /秒
LongHashMapTroveTraverse thrpt 1 3 5 351955.550 901.902 ops /秒
コレクション内の要素の数を減らすと、パフォーマンスのギャップが小さくなることがわかります。
コレクションが小さく、使用頻度が低い場合、プロジェクトに追加の依存関係を含めることに特別な意味はないと結論付けることができます。
しかし、プロジェクトがプリミティブ型の大規模なコレクションを大量に使用する場合は、Troveの使用を正当化できます。
プロジェクトコードは
GitHubで入手でき
ます 。
PS。 コレクションを作成するときの要素数の値は、意図的に設定されていません。