SparseArray ve HashMap Karşılaştırması


177

HashMapTamsayı tuşları ile s neden s çok daha iyi birkaç neden düşünebilirsiniz SparseArray:

  1. Android dokümanları SparseArray"Genellikle geleneksel olandan daha yavaş" diyor HashMap.
  2. Kodları HashMaps yerine SparseArrays kullanarak yazarsanız, kodunuz Map'in diğer uygulamalarıyla çalışır ve Haritalar için tasarlanmış tüm Java API'lerini kullanabilirsiniz.
  3. HashMapS yerine SparseArrays kullanarak kod yazarsanız, kodunuz android olmayan projelerde çalışacaktır.
  4. Harita geçersiz kılınır equals(), hashCode()oysa geçersiz kılınır SparseArray.

Yine HashMapde bir Android projesinde tamsayı tuşlarıyla bir anahtar kullanmaya çalıştığımda IntelliJ bana SparseArraybunun yerine bir anahtar kullanmam gerektiğini söylüyor . Bunu anlamak gerçekten zor. Herkes SparseArrays kullanmak için zorlayıcı nedenleri biliyor mu ?

Yanıtlar:


235

SparseArrayHashMapanahtar ilkel bir tip olduğunda değiştirmek için kullanılabilir . Her biri herkese açık olmasa da, farklı anahtar / değer türleri için bazı varyantlar vardır.

Yararları:

  • Tahsis içermeyen
  • Boks yok

Dezavantajları:

  • Genelde daha yavaş, büyük koleksiyonlar için belirtilmemiş
  • Android olmayan bir projede çalışmazlar

HashMap aşağıdaki ile değiştirilebilir:

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 

Bellek açısından, 1000 elemanlar için SparseIntArrayvs örneği HashMap<Integer, Integer>:

SparseIntArray:

class SparseIntArray {
    int[] keys;
    int[] values;
    int size;
}

Sınıf = 12 + 3 * 4 = 24 bayt
Dizi = 20 + 1000 * 4 = 4024 bayt
Toplam = 8,072 bayt

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;
}

Sınıf = 12 + 8 * 4 = 48 bayt
Giriş = 32 + 16 + 16 = 64 bayt
Dizi = 20 + 1000 * 64 = 64024 bayt
Toplam = 64,136 bayt

Kaynak: Slayt 90'dan Romain Guy tarafından Android Memories .

Yukarıdaki sayılar, JVM tarafından öbek üzerinde ayrılan bellek miktarıdır (bayt cinsinden). Kullanılan spesifik JVM'ye bağlı olarak değişebilirler.

java.lang.instrumentPaket ile bir nesnenin boyutunu kontrol gibi gelişmiş işlemleri için bazı yararlı yöntemler içerir getObjectSize(Object objectToSize).

Ekstra bilgi resmi Oracle belgelerinde bulunabilir .

Sınıf = 12 bayt + (n örnek değişkenleri) * 4 bayt
Dizi = 20 bayt + (n öğe) * (öğe boyutu)
Giriş = 32 bayt + (1. öğe boyutu) + (2. öğe boyutu)


15
Birisi bana "12 + 3 * 4" ve "20 + 1000 * 4" 'ün nereden geldiğini yönlendirebilir mi?
Marian Paździoch

5
@ MarianPaździoch, bir sınıf gösterdi ( speakerdeck.com/romainguy/android-memories ) burada bir sınıf 12 bayt + 3 değişken 4 bayt, bir dizi (referans) 20 bayt (dlmalloc - 4, nesne yükü - 8, genişlik ve dolgu) kaplar - 8).
CoolMind

1
Kayıt için SparseArray'ın bir diğer önemli dezavantajı, bir Android nesnesi olarak birim testi için alay edilmesi gerektiğidir. Mümkün olduğunda, artık testleri basitleştirmek için Java'nın kendi nesnelerini kullanıyorum.
David G

@DavidG Sadece unmock eklentisini kullanabilirsiniz Android bağımlılıklarını taklit etmek .
blizzard

1
Android yapmasanız bile, sınıfı projenize kopyalamak zor değildir, sadece 3 sınıfa bağlıdır. APL lisansı, çalıştığınız lisans ne olursa olsun bunu yapmanın uygun olduğu anlamına gelir.
Yann TM

35

Buraya sadece nasıl kullanılacağına dair bir örnek istemeye geldim SparseArray. Bu, bunun için ek bir cevaptır.

Bir SparseArray Oluşturun

SparseArray<String> sparseArray = new SparseArray<>();

A SparseArray, bazılarıyla tam sayı eşleştirir Object, böylece Stringyukarıdaki örnekte başka bir taneyle değiştirebilirsiniz Object. Tam sayıları tam sayılarla eşliyorsanız şunu kullanın:SparseIntArray .

Öğe ekle veya güncelle

Diziye eleman eklemek için put(veya append) tuşunu kullanın .

sparseArray.put(10, "horse");
sparseArray.put(3, "cow");
sparseArray.put(1, "camel");
sparseArray.put(99, "sheep");
sparseArray.put(30, "goat");
sparseArray.put(17, "pig");

intAnahtarların sırayla olması gerekmediğini unutmayın . Bu, belirli bir intanahtardaki değeri değiştirmek için de kullanılabilir .

Öğeleri kaldır

Dizideki öğeleri kaldırmak için remove(veya delete) tuşunu kullanın .

sparseArray.remove(17); // "pig" removed

intParametre tamsayı anahtarıdır.

İnt anahtarı için arama değerleri

getBazı tamsayı anahtarlarının değerini almak için kullanın .

String someAnimal = sparseArray.get(99);  // "sheep"
String anotherAnimal = sparseArray.get(200); // null

Kullanabilirsiniz get(int key, E valueIfKeyNotFound)nullEksik anahtarları almaktan kaçınmak istiyorsanız .

Öğelerin üzerinde yineleme

Sen kullanabilirsiniz keyAtve valueAtçünkü topluluk içinde döngü için bazı endeksi SparseArrayayrı ayrı bir dizin tutar inttuşları.

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

Anahtarların, eklendikleri sırayla değil, artan değerde sıralandığını unutmayın.


18

Yine de bir android projesinde tamsayı tuşlarıyla bir HashMap kullanmaya çalıştığımda, intelliJ bana bunun yerine SparseArray kullanmam gerektiğini söylüyor.

Sadece seyrek dizinin bu dokümantasyonundan bir uyarıdır :

Tamsayıları Nesnelerle eşlemek için HashMap kullanmaktan daha fazla bellek verimli olması amaçlanmıştır

SparseArrayOlacak şekilde yapılır etkili hafıza olup HashMap gibi bir dizi içinde birden çok boşluklar izin vermez normal HashMap kullanmak yerine,. Endişelenecek bir şey yok, aygıta bellek ayırma konusunda endişelenmemek istiyorsanız geleneksel HashMap'i kullanabilirsiniz.


5
Bellek tasarrufu ile ilgili noktalar açıkça geçerlidir, ancak android'in neden SparseArray <T> Harita <Tamsayı, T> uygulamasını yapamadığını anlayamadım, böylece her iki dünyanın da en iyisi olan bellek verimli bir Harita uygulaması elde edersiniz.
Paul Boddington

3
@PaulBoddington SparseArray, anahtar tamsayısının başka bir işlem ve maliyet performansı olan Otomatik kutusu olmasını önler. Harita yerine ilkel tamsayıyı otomatik olarak Integer
kutuya alacak

Ayrıca doğrudur, ancak imza koyma (int a, T t) içeren bir put yöntemi aşırı yüklemiş olsalardı, anahtar-değer çiftlerini, anahtarlar otomatik olarak kutulanmadan haritaya yerleştirebilirsiniz. Sadece Koleksiyon Çerçevesinin o kadar güçlü (Java kullanmanın en iyi nedenlerinden biri) olduğunu ve bundan faydalanmamak için delilik olduğunu düşünüyorum.
Paul Boddington

6
@PaulBoddington Koleksiyonlar ilkel olmayan nesnelere dayanır, bu yüzden Koleksiyonlar API'sında çalışmaz
Rod_Algonquin

10

Java'daki seyrek dizi, anahtarları değerlerle eşleştiren bir veri yapısıdır. Bir Harita ile aynı fikir, ancak farklı uygulama:

  1. Bir Harita, dahili olarak bir liste dizisi olarak temsil edilir; burada bu listelerdeki her öğe, bir anahtar, değer çiftidir. Hem anahtar hem de değer nesne örnekleridir.

  2. Seyrek dizi basitçe iki diziden oluşur: (temel öğeler) anahtar dizileri ve (nesne) değerleri dizisi. Bu diziler endekslerinde boşluklar olabilir, bu nedenle “seyrek” dizisi terimi.

SparseArray'ın ana ilgisi, anahtar olarak nesneler yerine ilkelleri kullanarak bellek tasarrufu yapmasıdır.


10

Bazı googling sonra zaten gönderilen anwers bazı bilgiler eklemek çalışın:

Isaac Taylor SparseArrays ve Hashmaps için bir performans karşılaştırması yaptı. O şunu belirtmektedir

Hashmap ve SparseArray, 1.000'in altındaki veri yapısı boyutları için çok benzer

ve

boyut 10.000 işaretine yükseltildiğinde [...] Hashmap nesne ekleme konusunda daha yüksek performans gösterirken, SparseArray nesneleri alırken daha yüksek performans gösterir. [...] 100.000 boyutunda [...] Hashmap performansı çok hızlı kaybediyor

Edgblog üzerinde yapılan bir karşılaştırma, bir SparseArray öğesinin daha küçük anahtar (int vs Integer) ve HashMap'ten çok daha az belleğe ihtiyaç duyduğunu gösterir.

bir HashMap.Entry örneği, anahtarın, değerin ve sonraki girişin başvurularını izlemelidir. Ayrıca, girişin karma değerini bir int olarak saklaması gerekir.

Sonuç olarak, Haritanızda çok fazla veri depolayacaksanız, farkın önemli olabileceğini söyleyebilirim. Aksi takdirde, uyarıyı dikkate almayın.


4

Bir SparseArray için Android belgeleri "Genellikle geleneksel bir HashMap daha yavaş" diyor.

Evet doğru. Ancak yalnızca 10 veya 20 öğeniz olduğunda, performans farkı önemsiz olmalıdır.

SparseArrays yerine HashMaps kullanarak kod yazarsanız, kodunuz diğer Map uygulamaları ile çalışır ve Haritalar için tasarlanmış tüm java API'lerini kullanabilirsiniz.

Çoğu zaman sadece HashMapbir anahtarla ilişkili bir değeri aramak için kullandığımızı düşünüyorum SparseArray.

SparseArrays yerine HashMaps kullanarak kod yazarsanız, kodunuz android olmayan projelerde çalışır.

SparseArray'ın kaynak kodu oldukça basit ve anlaşılması kolaydır, böylece onu diğer platformlara taşımak için çok az çaba sarf edersiniz (basit bir KOPYALA Yapıştır).

Harita, eşittir () ve hashCode () değerlerini geçersiz kılar, ancak SparseArray

Söyleyebileceğim tek şey, (çoğu geliştiriciye) kimin umurunda?

Bir diğer önemli yönü SparseArraysadece ederken tüm unsurları saklamak için bir dizi kullanmasıdır HashMapkullanır Entry, bu yüzden SparseArraybir daha belirgin az bellek maliyeti HashMap, bkz bu


1

Derleyicinin bir uyarı yayınlaması talihsiz bir durumdur. Sanırım HashMap öğeleri depolamak için aşırı kullanıldı.

SparseArrays'ın yeri var. Bir dizide bir değer bulmak için bir ikili arama algoritması kullandıkları göz önüne alındığında, ne yaptığınızı düşünmelisiniz. İkili arama O (log n) iken, karma arama O (1) olur. Bu, belirli bir veri kümesi için ikili aramanın daha yavaş olduğu anlamına gelmez. Ancak, girişlerin sayısı arttıkça, karma tablosunun gücü devralınır. Bu nedenle, düşük sayıda girişin bir HashMap kullanmaktan daha iyi olabileceği ve muhtemelen daha iyi olabileceği yorumlar.

Bir HashMap sadece karma kadar iyidir ve ayrıca yük faktörü tarafından etkilenebilir (bence sonraki sürümlerde yük faktörünü göz ardı eder, böylece daha iyi optimize edilebilir). Ayrıca karmaın iyi olduğundan emin olmak için ikincil bir karma eklediler. Ayrıca SparseArray'ın nispeten az sayıda giriş için gerçekten iyi çalışmasının nedeni (<100).

Bir karma tabloya ihtiyacınız varsa ve ilkel tamsayı (otomatik boks yok) vb. İçin daha iyi bellek kullanımı istiyorsanız, trove deneyin. ( http://trove.starlight-systems.com - LGPL lisansı). (Tıpkı kütüphane gibi trove ile ilişki yok)

Basitleştirilmiş multi-dex bina ile, ihtiyacınız olan şey için hazneyi yeniden paketlemenize bile gerek yok. (trove'nin çok fazla sınıfı vardır)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.