整数キーを持つHashMap
sがSparseArray
sよりもはるかに優れている理由はいくつか考えられます。
SparseArray
のAndroidドキュメントには、「従来のHashMap
より一般に遅い」と書かれています。HashMap
sではなくSparseArray
sを使用してコードを記述する場合、コードはMapの他の実装で機能し、Maps用に設計されたすべてのJava APIを使用できます。HashMap
sではなくSparseArray
sを使用してコードを記述する場合、コードはAndroid以外のプロジェクトで機能します。equals()
およびhashCode()
をオーバーライドしますが、SparseArray
はオーバーライドしません。しかし、Androidプロジェクトで整数キーでHashMap
を使用しようとするたびに、IntelliJは代わりにSparseArray
を使用するように指示します。これを理解するのは本当に難しいと思います。 SparseArray
sを使用する説得力のある理由を誰か知っていますか?
SparseArray
は、キーがプリミティブ型の場合に HashMap
を置き換えるために使用できます。すべてのキー/値タイプが公開されているわけではありませんが、さまざまなキー/値タイプ用のバリアントがいくつかあります。
利点は次のとおりです。
欠点:
HashMap
は、次のものに置き換えることができます。
SparseArray <Integer, Object>
SparseBooleanArray <Integer, Boolean>
SparseIntArray <Integer, Integer>
SparseLongArray <Integer, Long>
LongSparseArray <Long, Object>
LongSparseLongArray <Long, Long> //this is not a public class
//but can be copied from Android source code
メモリに関しては、1000個の要素に対するSparseIntArray
とHashMap<Integer, Integer>
の例を次に示します。
SparseIntArray
:
class SparseIntArray {
int[] keys;
int[] values;
int size;
}
クラス= 12 + 3 * 4 = 24バイト
配列= 20 + 1000 * 4 = 4024バイト
合計= 8,072バイト
HashMap
:
class HashMap<K, V> {
Entry<K, V>[] table;
Entry<K, V> forNull;
int size;
int modCount;
int threshold;
Set<K> keys
Set<Entry<K, V>> entries;
Collection<V> values;
}
クラス= 12 + 8 * 4 = 48バイト
エントリ= 32 + 16 + 16 = 64バイト
配列= 20 + 1000 * 64 = 64024バイト
合計= 64,136バイト
出典: Romain GuyによるAndroid Memories スライド90から。
上記の数値は、JVMによってヒープに割り当てられたメモリの量(バイト単位)です。それらは、使用される特定のJVMによって異なる場合があります。
Java.lang.instrument
パッケージには、オブジェクトのサイズをgetObjectSize(Object objectToSize)
でチェックするなどの高度な操作に役立つメソッドがいくつか含まれています。
追加情報は、公式の Oracleドキュメント から入手できます。
クラス= 12バイト+(nインスタンス変数)* 4バイト
配列= 20バイト+(n要素)*(要素サイズ)
エントリ= 32バイト+(最初の要素サイズ)+(2番目の要素サイズ)
SparseArray
の使用方法の例を求めてここに来ました。これはそのための補足的な答えです。
SparseArray<String> sparseArray = new SparseArray<>();
SparseArray
は整数をいくつかのObject
にマップするため、上記の例のString
を他のObject
に置き換えることができます。整数を整数にマッピングする場合は、 SparseIntArray
を使用します。
put
(または append
)を使用して、配列に要素を追加します。
sparseArray.put(10, "horse");
sparseArray.put(3, "cow");
sparseArray.put(1, "camel");
sparseArray.put(99, "sheep");
sparseArray.put(30, "goat");
sparseArray.put(17, "pig");
int
キーは順番に並んでいる必要はありません。これは、特定のint
キーの値を変更するためにも使用できます。
remove
(または delete
)を使用して、配列から要素を削除します。
sparseArray.remove(17); // "pig" removed
int
パラメーターは整数キーです。
get
を使用して、整数キーの値を取得します。
String someAnimal = sparseArray.get(99); // "sheep"
String anotherAnimal = sparseArray.get(200); // null
欠落しているキーに対してnull
を取得しないようにする場合は、 get(int key, E valueIfKeyNotFound)
を使用できます。
keyAt
はvalueAt
キーとは別のインデックスを保持するため、 SparseArray
および int
を使用してコレクションをループできます。
int size = sparseArray.size();
for (int i = 0; i < size; i++) {
int key = sparseArray.keyAt(i);
String value = sparseArray.valueAt(i);
Log.i("TAG", "key: " + key + " value: " + value);
}
// key: 1 value: camel
// key: 3 value: cow
// key: 10 value: horse
// key: 30 value: goat
// key: 99 value: sheep
キーは、追加された順序ではなく、昇順で並べられていることに注意してください。
しかし、Androidプロジェクトで整数キーでHashMapを使用しようとするたびに、intelliJは代わりにSparseArrayを使用する必要があることを通知します。
これは単なる警告です ドキュメント スパース配列:
HashMapを使用して整数をオブジェクトにマッピングするよりもメモリ効率が高いことを目的としています
SparseArray
は、通常のHashMapを使用するよりもメモリ効率がよくなるように作成されます。つまり、HashMapのように配列内に複数のギャップを許可しません。デバイスへのメモリ割り当てを気にしない場合は、従来のHashMapを使用できますので、心配する必要はありません。
いくつかのグーグルの後、私はすでに投稿されたanwersにいくつかの情報を追加しようとします:
Isaac Taylor SparseArraysとHashmapsのパフォーマンスを比較しました。彼は次のように述べています
hashmapとSparseArrayは、1,000未満のデータ構造サイズについて非常に似ています
そして
サイズが10,000マークまで増加した場合[...]ハッシュマップはオブジェクトを追加する際のパフォーマンスが向上し、SparseArrayはオブジェクトを取得する際のパフォーマンスが向上します。 [...] 100,000のサイズで[...]ハッシュマップのパフォーマンスが急速に低下します
Edgblog の比較では、SparseArrayが必要とするメモリがHashMapよりもはるかに少ないことを示しています。これは、キーが小さいため(int対Integer)、
hashMap.Entryインスタンスは、キー、値、および次のエントリの参照を追跡する必要があります。さらに、エントリのハッシュをintとして保存する必要もあります。
結論として、マップに大量のデータを保存する場合、違いが重要になると思います。それ以外の場合は、警告を無視してください。
Javaのスパース配列は、キーを値にマッピングするデータ構造です。マップと同じアイデアですが、実装が異なります。
Mapは内部的にリストの配列として表され、これらのリストの各要素はキーと値のペアです。キーと値の両方がオブジェクトインスタンスです。
スパース配列は、(プリミティブ)キーの配列と(オブジェクト)値の配列という2つの配列で構成されています。これらの配列インデックスにはギャップが存在する可能性があるため、「疎」配列という用語が使用されます。
SparseArrayの主な目的は、オブジェクトの代わりにプリミティブをキーとして使用してメモリを節約することです。
SparseArrayのAndroidドキュメントには、「従来のHashMapよりも一般に遅い」と書かれています。
はい、そうです。ただし、アイテムが10個または20個しかない場合、パフォーマンスの違いはわずかです。
SparseArraysではなくHashMapsを使用してコードを記述する場合、コードはMapの他の実装で機能し、Maps用に設計されたJava APIをすべて使用できます。
ほとんどの場合、HashMap
はキーに関連付けられた値を検索するためにのみ使用しますが、SparseArray
はこれが得意です。
SparseArraysではなくHashMapsを使用してコードを記述する場合、コードはAndroid以外のプロジェクトで機能します。
SparseArrayのソースコードは非常にシンプルで理解しやすいため、他のプラットフォームに移動するのに少しの労力しかかかりません(簡単なCOPY&Pasteを使用)。
マップはequals()およびhashCode()をオーバーライドしますが、SparseArrayはオーバーライドしません
私が言えることは、(ほとんどの開発者にとって)誰が気にするか?
SparseArray
のもう1つの重要な側面は、HashMap
がEntry
を使用するのに対し、配列を使用してすべての要素を格納することです。したがって、SparseArray
はHashMap
、 this を参照
コンパイラが警告を発行するのは残念です。 HashMapはアイテムの保存に過度に使用されていると思います。
SparseArraysには場所があります。バイナリ検索アルゴリズムを使用して配列内の値を見つける場合、何をしているのかを考慮する必要があります。バイナリ検索はO(log n)であり、ハッシュルックアップはO(1)です。これは、必ずしも特定のデータセットに対してバイナリ検索が遅いという意味ではありません。ただし、エントリの数が増えると、ハッシュテーブルの能力が引き継ぎます。したがって、エントリの数が少ない場合のコメントは、HashMapを使用するよりも優れている可能性があります。
HashMapはハッシュと同程度に優れているだけでなく、負荷係数の影響も受ける可能性があります(後のバージョンでは、負荷係数を無視するため、最適化が改善されると思います)。また、ハッシュが適切であることを確認するためにセカンダリハッシュを追加しました。また、SparseArrayが比較的少ないエントリ(<100)で本当にうまく機能する理由もあります。
ハッシュテーブルが必要で、プリミティブ整数(オートボクシングなし)などのメモリ使用量を増やしたい場合は、troveを試してみることをお勧めします。 ( http://trove.starlight-systems.com -LGPLライセンス)。 (彼らのライブラリのように、Troveとの提携はありません)
簡素化されたマルチデックスビルディングにより、必要なもののためにtroveを再パッケージ化する必要さえありません。 (troveには多くのクラスがあります)