文章目录
常用数据结构
List,Set,Map的区别
Set是最简单的一种集合。集合中的对象不按特定的方式排序,并且没有重复对象。 Set接口主要实现了两个实现类:HashSet: HashSet类按照哈希算法来存取集合中的对象,存取速度比较快
List的特征是其元素以线性方式存储,集合中可以存放重复对象。
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口 从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
数组和链表的区别
-
数组:
处理一组数据类型相同的数据。但是不允许动态定义数组的大小,即在使用数组之前必须确定数组的大小。这样数组中的有些空间可能不被使用,从而造成内存空间的浪费。当数据增加时,可能超出原先定义的元素个数,造成数组越界。数组插入删除时需要移动其他数据项。但是查询方便。
数组从栈中分配空间,对于程序员方便快速,数组无需初始化,因为数组元素在内存的栈区,系统自动申请空间。但是自由度小。数组元素在内存中连续 -
链表:
链表动态的进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。链表插入和删除时,只需要改变个别元素之间的关系,这大大提高了链表的删除和插入的速度。查询时需要从前往后遍历。
链表从堆中分配空间,自由度大,但是申请管理比较麻烦,链表的结点元素在内存的堆区,每个元素须手动申请空间。链表在内存中不一定连续。
ArrayList
简介
/**
* Default initial capacity.
* ArrayList 默认的数组容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
* 用于空实例的共享空数组实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
* 另一个共享空数组实例,用的不多,用于区别上面的EMPTY_ELEMENTDATA
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
* ArrayList底层的容器
*/
// Android-note: Also accessed from java.util.Collections
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
* 当前存放了多少个元素 并非数组大小
*/
private int size;
ArrayList是基于动态数组的数据结构。实现了list接口,是以数组的方式实现的。所谓动态数组是这样实现的,如果没有指定数组的大小,则申请默认大小为10 的数组,当元素个数增加,数据无法存储时,系统会另外申请一个长度为当前长度的1.5倍的数组,然后把之前的数据拷贝到新建的数组中。
数组的特征是可以使用索引的方式来快速定位对象的位置。适合读取数据。
ArrayList源码分析
- 添加
/**
* 添加指定元素到末尾
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(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
//1. 记录之前的数组长度
int oldCapacity = elementData.length;
//2. 新数组的大小=老数组大小+老数组大小的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
//3. 判断上面的扩容之后的大小newCapacity是否够装minCapacity个元素
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//4.判断新数组容量是否大于最大值
//如果新数组容量比最大值(Integer.MAX_VALUE - 8)还大,那么交给hugeCapacity()去处理,该抛异常则抛异常
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
//5. 复制数组,注意,这里是浅复制
elementData = Arrays.copyOf(elementData, newCapacity);
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
思路:
1.首先判断添加一个元素是否会导致数组溢出
2.判断是否溢出:如果原数组为空,那么第一次添加数组时会给数组一个默认大小10,接着判断是否溢出,如果溢出则去扩容,扩容规则:新数组是原数组大小的1.5倍,最后通过Arrays.copyOf()去浅复制
3.添加元素到末尾
- 获取
/**
* 返回指定位置处元素
*/
public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
return (E) elementData[index];
}
思路:通过下标获得数组中的元素
- 移除
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
E oldValue = (E) elementData[index];
// 复制的长度
int numMoved = size - index - 1;
if (numMoved > 0)
//数组之间的复制
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public static native void arraycopy (Object src,//源数组
int srcPos,//源数组要复制的起始位置
Object dest,//目的数组
int destPos,//目的