携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第28天,点击查看活动详情
一、java集合工具类
java工具包中为我们提供了集合工具类,如下
1-1、ArrayList
ArrayList是我们常用的存储数据的集合,那它有什么特点、底层又是如何存储数据的呢?
特点:
元素有序的存储,并且可以重复
存储结构 底层采用数组来实现
原理
数组采用一段连续的存储单元来存储数据的,其特点为查询O(1)(查询的次数固定可循),删除插入为O(N)(查询的次数不确定),即查询快,插入删除慢,下面来说一下为什么
1-1-1、数组的读取、插入、删除实现过程
首先来看下数组的存储结构,定义的变量存储在栈区,而数据存储在堆区的连续地址中,通过指向堆区的内存地址来进行使用。
1-1-1-1、查询-(O(1)-速度快)
在一维数组中,存储了4个int类型的值,起始位置从100开始,因为int的字节数为4,因此后面的元素依次+4。
查找元素的计算公式为:a[n]=起始地址+(n*字节数)
比如要查找下标为2的元素,那就是a[2]=100+(2*4),得到108位置,这样a[2]的值就可以查询到了
1-1-1-2、插入-(O(N)-速度慢)
向数组中某个下标插入值,那么原下标及其后的值的下标地址就都需要依次向后移动。在元素不固定的情况下,我们插入数据,数组中的元素移动下标位置数量是不固定的,这就是O(N)。
1-1-1-3、删除-(O(N)-速度慢)
和插入类似,在根据下标删除的时候,后面的数据就需要依次向前移动元素,数组不确定,移动的数量也就不确定,因此数组删除元素也是O(N)
1-1-2、Arraylist.add(E e)-O(1)
首先看下ArrayList.add的源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
通过以上可以看到首先先调用了ensureCapacityInternal这个是一个扩容的方法,进入看下
ensureCapacityInternal->ensureExplicitCapacity->grow,在grow方法中最终实现的是Arrays.copyOF数组拷贝的方法
这个地方数组拷贝时间上就是用空间换时间的方式,比如元素组有10个位置的空间,在添加元素的时候,不是对元素进行扩容,而是申请一个新的地址,将原数组的内容复制到新地址中,然后再add新的数据
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
1-1-3、Arraylist.add(int index, E element)-O(N)
这种add方法就是O(N)操作,因为需要指定添加元素的下标,那么就涉及到数组元素的移位,操作时间就会增加
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
1-1-4、ArrayList.get(i)
可以看到get(i),实际上是一个随机获取的方法
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
E elementData(int index) {
return (E) elementData[index];
}