0%

Android-SparseArray

SparseArray 是Android里面加入的数据结构,在插入的时候会对Key做hash,然后按照二分查找的算法去找到合适的位置插入或者更新对应的value值。和Hashmap相比较的话,用到的内存会比散列表小,因为少了那部分阈值的空数组位,但是数据量大起来后,二分查找比起散列表直接hash取位置还是要慢一些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
//内部维护一个int[]类型的key数组,一个object[]类型的value数组,key会保持有序并且无重复
public class SparseArray<E> implements Cloneable {
private static final Object DELETED = new Object(); //用于删除的标记
private boolean mGarbage = false;
 
private int[] mKeys;
private Object[] mValues;
private int mSize;
 
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;
}
 
@Override
public SparseArray<E> clone() {
SparseArray<E> clone = null;
try {
clone = (SparseArray<E>) super.clone();
clone.mKeys = mKeys.clone();
clone.mValues = mValues.clone();
} catch (CloneNotSupportedException cnse) {
/* ignore */
}
return clone;
}
 
public E get(int key) {
return get(key, null);
}

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];
}
}
 
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
if (i >= 0) {
if (mValues[i] != DELETED) {
mValues[i] = DELETED;
mGarbage = true;
}
}
}
 
public void remove(int key) {
delete(key);
}
 
public void removeAt(int index) {
if (mValues[index] != DELETED) {
mValues[index] = DELETED;
mGarbage = true;
}
}
 
public void removeAtRange(int index, int size) {
final int end = Math.min(mSize, index + size);
for (int i = index; i < end; i++) {
removeAt(i);
}
}
 
//把数组标记为DELETED的元素置为null,非标记的元素向前移动,然后更新属性
private void gc() {
int n = mSize;
int o = 0;
int[] keys = mKeys;
Object[] values = mValues;
 
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++;
}
}
 
mGarbage = false;
mSize = o;
  }
 
//首先会查找,如果找到了相同的key,会直接替换value的值,因此不会存在相同的key的情况,没找到的情况下,如果i<size并且最后一次二分查找的低位标记为DELETED的话,直接把key,value赋值过去,否则gc()一次,然后调用工具类把key,value插入到i位置,这样的话key依然是有序的
public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
if (i >= 0) {
mValues[i] = value;
} else {
i = ~i;
 
if (i < mSize && mValues[i] == DELETED) {
mKeys[i] = key;
mValues[i] = value;
return;
}
 
if (mGarbage && mSize >= mKeys.length) {
gc();
 
// Search again because indices may have changed.
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
 
mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
mValues = GrowingArrayUtils.insert(mValues, mSize, i, value);
mSize++;
}
}
 
public int size() {
if (mGarbage) {
gc();
}
 
return mSize;
}
 
public int keyAt(int index) {
if (mGarbage) {
gc();
}
 
return mKeys[index];
}
 
public E valueAt(int index) {
if (mGarbage) {
gc();
}
 
return (E) mValues[index];
}
 
public void setValueAt(int index, E value) {
if (mGarbage) {
gc();
}
 
mValues[index] = value;
}
 
public int indexOfKey(int key) {
if (mGarbage) {
gc();
}
 
return ContainerHelpers.binarySearch(mKeys, mSize, key);
}
 
public int indexOfValue(E value) {
if (mGarbage) {
gc();
}
 
for (int i = 0; i < mSize; i++)
if (mValues[i] == value)
return i;
 
return -1;
}
 
public void clear() {
int n = mSize;
Object[] values = mValues;
 
for (int i = 0; i < n; i++) {
values[i] = null;
}
 
mSize = 0;
mGarbage = false;
}
 
public void append(int key, E value) {
if (mSize != 0 && key <= mKeys[mSize - 1]) {
put(key, value);
return;
}
 
if (mGarbage && mSize >= mKeys.length) {
gc();
}
 
mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
mValues = GrowingArrayUtils.append(mValues, mSize, value);
mSize++;
}
 
@Override
public String toString() {
if (size() <= 0) {
return "{}";
}
 
StringBuilder buffer = new StringBuilder(mSize * 28);
buffer.append('{');
for (int i=0; i<mSize; i++) {
if (i > 0) {
buffer.append(", ");
}
int key = keyAt(i);
buffer.append(key);
buffer.append('=');
Object value = valueAt(i);
if (value != this) {
buffer.append(value);
} else {
buffer.append("(this Map)");
}
}
buffer.append('}');
return buffer.toString();
}



----------------------------------------------------------------------
//上面用到的工具类的二分查找算法
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; // value found
}
}
return ~lo; // value not present 取反为负数,再取反可以获取二分的最后一次位置,直接插入新的值
}