日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java填坑系列之SparseArray

發(fā)布時間:2025/6/15 java 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java填坑系列之SparseArray 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

今天我們來了解一下與HashMap類似的數(shù)據(jù)結(jié)構(gòu)SparseArray,并分析下它的源碼實現(xiàn)。在分析源碼的過程中,我們帶著以下幾個問題來看。

  • SparseArray底層數(shù)據(jù)結(jié)構(gòu)是什么?
  • SparseArray如何通過key獲得對應(yīng)數(shù)組下標(biāo)
  • SparseArray的擴(kuò)容機(jī)制是什么?
  • SparseArray與HashMap有什么區(qū)別?

核心字段

private static final Object DELETED = new Object();private boolean mGarbage = false;private int[] mKeys;private Object[] mValues;private int mSize; 復(fù)制代碼

首先我們先來了解一下SparseArray類中聲明的變量都是做什么的,如下

  • DELETED 表示刪除狀態(tài)(后面詳細(xì)說明)
  • mGarbage 表示是否GC
  • mKeys 表示Key數(shù)組,SparseArray中專門存取Key的數(shù)組
  • mValues 表示Values數(shù)組,SparseArray中專門存取Value的數(shù)組
  • mSize 表示數(shù)組實際存儲的元素大小

小結(jié)

通過了解以上幾個變量,我們可以大概知道SparseArray底層是通過兩個數(shù)組來實現(xiàn)的,一個int數(shù)組來存取Key,一個Object數(shù)組來存取Value。

構(gòu)造方法

public SparseArray() {this(10);}public SparseArray(int initialCapacity) {if (initialCapacity == 0) {mKeys = EmptyArray.INT;mValues = EmptyArray.OBJECT;} else {mValues = ArrayUtils.newUnpaddedObjectArray(initialCapacity);mKeys = new int[mValues.length];}mSize = 0;} 復(fù)制代碼

可以看出SparseArray默認(rèn)容量為10,然后我們來看一下put()方法。

put()

public void put(int key, E value) {//通過key獲取對應(yīng)的數(shù)組位置int i = ContainerHelpers.binarySearch(mKeys, mSize, key);//若i>=0,說明key存在,直接賦值if (i >= 0) {mValues[i] = value;} else {//此時i<0,然后對i取反i = ~i;//如果i<mSize并且i對應(yīng)的Value已經(jīng)是標(biāo)記位刪除狀態(tài),那么就復(fù)用這個位置if (i < mSize && mValues[i] == DELETED) {mKeys[i] = key;mValues[i] = value;return;}//如果需要GC并且mSize大于等于mKeys數(shù)組的長度,那么進(jìn)行GC,并且重新查找iif (mGarbage && mSize >= mKeys.length) {gc();i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);}//最后分別插入key和value,并且判斷是否需要擴(kuò)容mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);mSize++;}} 復(fù)制代碼

通過分析put()方法源碼,我們可以知道SparseArray添加元素可以分為以下幾步。

  • 獲取key對應(yīng)的數(shù)組位置i
  • 判斷i是否大于0
    • 如果i>0,說明key存在,直接賦值
    • 如果i<0,說明key不存在
      • 如果i<mSize并且i對應(yīng)的Value已經(jīng)是標(biāo)記位刪除狀態(tài),那么就復(fù)用這個位置
      • 如果需要GC并且mSize大于等于mKeys數(shù)組的長度,那么進(jìn)行GC,并且重新查找i
      • 最后分別插入key和value,并且判斷是否需要擴(kuò)容

查找key對應(yīng)的數(shù)組下標(biāo)

static int binarySearch(int[] array, int size, int value) {int lo = 0;int hi = size - 1;while (lo <= hi) {final int mid = (lo + hi) >>> 1;final int midVal = array[mid];if (midVal < value) {lo = mid + 1;} else if (midVal > value) {hi = mid - 1;} else {return mid; }}return ~lo; } 復(fù)制代碼

可以看出通過二分查找的方式來獲得key在數(shù)組中對應(yīng)的下標(biāo),最后如果沒找到,會對lo取反并返回。

GC相關(guān)方法

private void gc() { //表示實際大小int n = mSize;int o = 0;int[] keys = mKeys;Object[] values = mValues;//遍歷所有元素,如果某個元素標(biāo)記為DELETED,那么就刪除for (int i = 0; i < n; i++) {Object val = values[i];if (val != DELETED) {if (i != o) {keys[o] = keys[i];values[o] = val;values[i] = null;}o++;}}//設(shè)為false,表示不需要GCmGarbage = false;mSize = o;} 復(fù)制代碼

擴(kuò)容機(jī)制

在插入key和value時調(diào)用了GrowingArrayUtils的insert()方法,然后我們來看一下SparseArray里如何進(jìn)行擴(kuò)容的,源碼如下。

public static int[] insert(int[] array, int currentSize, int index, int element) {assert currentSize <= array.length;//不需要擴(kuò)容if (currentSize + 1 <= array.length) {//從index以后的元素向后移一位System.arraycopy(array, index, array, index + 1, currentSize - index);//在index對應(yīng)的位置賦值array[index] = element;return array;}//需要擴(kuò)容,創(chuàng)建一個新數(shù)組int[] newArray = ArrayUtils.newUnpaddedIntArray(growSize(currentSize));//將array數(shù)組中從0到index之間的元素(不包括index對應(yīng)的元素)復(fù)制到新數(shù)組中System.arraycopy(array, 0, newArray, 0, index);newArray[index] = element;//再將index之后的元素復(fù)制到新數(shù)組中System.arraycopy(array, index, newArray, index + 1, array.length - index);return newArray;}public static int growSize(int currentSize) {//如果currentSize小于等于4,就為8,否則乘以2return currentSize <= 4 ? 8 : currentSize * 2;} 復(fù)制代碼

通過分析以上方法的源碼,我們知道了SparseArray的擴(kuò)容機(jī)制,主要步驟如下。

  • 創(chuàng)建一個新數(shù)組,數(shù)組容量根據(jù)currentSize來判斷
  • 將舊數(shù)組中,index之前的數(shù)組元素復(fù)制到新數(shù)組中
  • 對新數(shù)組中的index對應(yīng)的元素進(jìn)行賦值
  • 將舊數(shù)組中,index之后的數(shù)組元素復(fù)制到新數(shù)組中

刪除操作

我們再來看一下SparseArray的刪除方法,通過查看源碼可以發(fā)現(xiàn)有多個刪除方法,我們一個個的來看一下。

remove(int key)

public void remove(int key) {delete(key);} public void delete(int key) {//通過key獲得對應(yīng)的數(shù)組下標(biāo)iint i = ContainerHelpers.binarySearch(mKeys, mSize, key);if (i >= 0) {//如果mValues[i]沒有被標(biāo)記為DELETED,那么就進(jìn)行標(biāo)記,并設(shè)置mGarbage為true,表示需要GCif (mValues[i] != DELETED) {mValues[i] = DELETED;mGarbage = true;}}} 復(fù)制代碼

可以看出該方法是通過key來進(jìn)行刪除,主要分為以下幾步。

  • 獲得key對應(yīng)的數(shù)組下標(biāo)
  • 如果i>=0,判斷mValues[i]是否被標(biāo)記為DELETED
    • 如果沒有被標(biāo)記為DELETED,那么就進(jìn)行標(biāo)記,并設(shè)置mGarbage為true,表示需要GC

removeAt(int index)

public void removeAt(int index) {if (mValues[index] != DELETED) {mValues[index] = DELETED;mGarbage = true;}} 復(fù)制代碼

通過index找到對應(yīng)的位置進(jìn)行刪除操作。

查找

get(int key)

public E get(int key) {return get(key, null);}@SuppressWarnings("unchecked")public E get(int key, E valueIfKeyNotFound) {int i = ContainerHelpers.binarySearch(mKeys, mSize, key);if (i < 0 || mValues[i] == DELETED) {return valueIfKeyNotFound;} else {return (E) mValues[i];}}復(fù)制代碼

從以上源碼可以看出SparseArray通過key來查找對應(yīng)的元素,主要有以下幾步。

  • 獲取key對應(yīng)的數(shù)組下標(biāo)
  • 如果i小于0或者mValues[i]已經(jīng)標(biāo)記為DELETED,返回valueIfKeyNotFound,也就是null
  • 否則就返回mValues[i]

SparseArray與HashMap區(qū)別

性能

在性能方面,SparseArray適合存儲少量的數(shù)據(jù)。如果存儲大量的數(shù)據(jù),在進(jìn)行擴(kuò)容時,會有比較大的性能開銷。

底層數(shù)據(jù)結(jié)構(gòu)

SparseArray是由兩個數(shù)組組成,一個數(shù)組負(fù)責(zé)存儲key(int類型),一個數(shù)組負(fù)責(zé)存儲value,類似于key為int類型的HashMap。

刪除

SparseArray的刪除操作先將要刪除的數(shù)組元素標(biāo)記為DELETED,然后當(dāng)存儲相同key對應(yīng)的value時,可以進(jìn)行復(fù)用。

總結(jié)

SparseArray在內(nèi)存方面上比HashMap消耗更少,所以對于數(shù)據(jù)不大的情況下,優(yōu)先使用SparseArray,畢竟在Android中內(nèi)存是比較重要的。

轉(zhuǎn)載于:https://juejin.im/post/5ca31d4bf265da30bf15ccdb

總結(jié)

以上是生活随笔為你收集整理的Java填坑系列之SparseArray的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。