嗯,准备好好巩固一下自己的数据结构,第一站就是数组,这是一个很基础很基础的话题。不过还是要说一下,单纯的数组我也想不到有什么好说的,就看看ArryList
吧。
Java
中的ArrayList
是我们经常用到的一个类,它内部就是利用数组来实现的。不知道你有没有研究过呢?其实并不复杂。看过来看过去,主要就是System.arraycopy();
ArraryList
的初始化无它,你可以指定一个初始的容量也可以不指定,如果不指定的话,则默认初始化一个长度为0的Object
数组。当然,你也可以利用另外一个集合来初始化ArrayLsit
。
public ArrayList(Collection extends E> collection) {
if (collection == null) {
throw new NullPointerException("collection == null");
}
Object[] a = collection.toArray();
if (a.getClass() != Object[].class) {
Object[] newArray = new Object[a.length];
System.arraycopy(a, 0, newArray, 0, a.length);// copy。。。
a = newArray;
}
array = a;
size = a.length;
}
1.增。增加有两个方法,指定下标或不指定下标。我们直接看指定下标的方法。
@Override
public void add(int index, E object) {
Object[] a = array;//当前的数组
int s = size;
if (index > s || index < 0) {
throwIndexOutOfBoundsException(index, s);
}
if (s < a.length) {// 从index开始的元素集体copy至index+1开始往后的位置
System.arraycopy(a, index, a, index + 1, s - index);
} else {
// assert s == a.length;
// 数组长度不够,就new一个新的数组
Object[] newArray = new Object[newCapacity(s)];
//将0至(index-1)位置的元素拷贝至新数组
System.arraycopy(a, 0, newArray, 0, index);
//将index开始往后的元素拷贝至新数组
System.arraycopy(a, index, newArray, index + 1, s - index);
array = a = newArray;// 指向新数组
}
a[index] = object;//指定位置元素赋值
size = s + 1;
modCount++;// 编辑次数,很少用到
}
// 扩容规则 MIN_CAPACITY_INCREMENT = 12。返回新数组的长度
private static int newCapacity(int currentCapacity) {
int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
return currentCapacity + increment;
}
当然,还有一个addAll(int index, Collection extends E> collection)
方法,相当于把新集合的元素依次添加至指定位置。和上面原理相似,不多说了。
2.删。删也有两种,一个是指定下标,另一个是指定元素。来看下~
@Override
public E remove(int index) {
Object[] a = array;
int s = size;
if (index >= s) {
throwIndexOutOfBoundsException(index, s);
}
// 不神秘,拿到指定index的元素,让最后返回
@SuppressWarnings("unchecked") E result = (E) a[index];
// 将index+1开始往后的元素拷贝至index开始的位置。注意 --s
System.arraycopy(a, index + 1, a, index, --s - index);
a[s] = null; // Prevent memory leak。末尾元素置null,防止内存泄漏
size = s;
modCount++;
return result;
}
@Override
public boolean remove(Object object) {
// 这个原理同上,不过要注意只会删除该元素第一次出现的位置。返回值:是否成功删除
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
}
return false;
}
删除里,还有一个clear()
,这个就是直接将所有元素置null
,size置0。
3.剩下的改和查就没什么要说的了。遍历数组,想必都会。
SparseArray
SparseArray
和ArrayList
都是利用数组来实现的,不过SparseArray
是以键值对的形式存储数据的,类似于Map
。
SparseArray
中维护了两个数组,一个keys,一个values。
通过看源码发现,SparseArray内部keys的存储是排序过的,CRUD的时候是通过二分查找,先找到key对应的index,然后就能找到对应index的value,所以效率会稍微高一些。需要注意的是,它的删除并不会将该位置元素置null
,而是放置一个常量Obj对象。其余的实现原理无非也是copy来copy去。不多说了。有兴趣的大家可以自己看下源码。
哦哦,有一个有意思的地方,就是gc()
。因为它删除的时候,并不直接置null,所以有个垃圾回收。
private void gc() {
// Log.e("SparseArray", "gc start with " + mSize);
int n = mSize;
int o = 0;
int[] keys = mKeys;
Object[] values = mValues;
for (int i = 0; i < n; i++) {
Object val = values[i];
// DELETED 就是那个常量Obj对象
if (val != DELETED) {
if (i != o) {
keys[o] = keys[i];
values[o] = val;
values[i] = null;
}
o++;
}
}
mGarbage = false;// 是否还有垃圾
mSize = o;
// Log.e("SparseArray", "gc end with " + mSize);
}
关于数组,目前能想到的就这么多了。很基础,一定要烂熟。