在休憩不知道该啃哪根骨头的情况下,选择了一根比较比较好啃的骨头,就是从Java基础知识开始,当然是源码,而集合就自然成为了要研究和记录了第一个源码(记录单纯是为了可以给自己看到一个学习轨迹,不足的地方有待大家提意见)
由于接下来会陆陆续续研究集合的源码,因此还是很传统的把这张图给贴出来了
六个接口:
Iterator,接口
Collection<E>,接口, Collection<E> extends Iterable<E>
Map ,接口 Map<K,V>
ListIterator<E>,接口, ListIterator<E> extends Iterator<E>
List, 接口, List<E> extends Collection<E>
Set,接口,Set<E> extends Collection<E>
SortedMap<K, V> ,接口,SortedMap<K,V> extends Map<K,V>
Collection的继承结构:
Collection<--List<--
Vector
Collection<--List<--
ArrayList
Collection<--List<--LinkedList
Collection<--Set<--
HashSet
Collection<--Set<--HashSet<--LinkedHashSet
Collection<--Set<--SortedSet<--TreeSet
一、ArrayList(线性列表)
一、ArrayList(线性列表)
说明:底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的
1、Vector、ArrayList 都是以类似数组的形式存储在内存中,LinkedList 则以链表的形式进行存储。
2、Vector 线程同步,ArrayList、LinkedList 线程不同步;后两者一般用在线程安全的地方,也可以通过 Collections.synchronizedList(……);实现线程同步。
3、LinkedList 适合指定位置插入、删除操作,不适合查找;ArrayList、Vector适合查找,不适合指定位置的插入、删除操作。
4、ArrayList 在元素填满容器 时会自动扩充容器大小的 50%,而 Vector 则是100%,因此 ArrayList 更节省空间。
5、LinkedList 还实现了 Queue 接口,该接口比 List 提供了更多的方法,包括offer(),peek(),poll()等,多与一些线程池一起使用.
补充:以上都实现了序列化接口,这点在对象远程传输时很重要;而Vector,ArrayList 实现了 AccessRandom 接口,这是一个标记接口,此接口的主要目的是允许 一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能
2.1 类的继承
class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess(可随机访问),
Cloneable(可拷贝), java.io.Serializable(序列化)
2.2 类的属性
//序列化编号
private static final long serialVersionUID = 8683452581122892189L;
//初始容量
private static final int DEFAULT_CAPACITY = 10;
//用于空实例的共享空数组实例。
private static final Object[] EMPTY_ELEMENTDATA = {};
//共享空数组实例,用于默认大小的空实例。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//存储ArrayList元素的数组缓冲区。
transient Object[] elementData;
protected transient int modCount = 0;
//数组实际大小
private int size;
//要分配的数组的最大大小。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2.3 核心方法
1) add()方法
public boolean add(E e) { //增加元素
ensureCapacityInternal(size + 1); // 调用ensureCapacityInternal方法
elementData[size++] = e;
return true;
}
这只是第一步,接下来是ensureCapacityInternal(int minCapacity)方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //在初期时默认赋值minCapacity = 10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity); //调用ensureExplicitCapacity方法
}
然后是ensureExplicitCapacity(int minCapacity)方法
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0) //是否为容器增容,初始为数组创建默认长度为10的空间
grow(minCapacity);
}
最后是grow(int minCapacity)
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量为旧容量的1.5倍(右位移)
if (newCapacity - minCapacity < 0) //新容量小于指定参数容量
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //新容量大于最大容量
newCapacity = hugeCapacity(minCapacity);
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;
}
2) set(int index,E elemnet)方法由于ArrayList的本质是Object[],因此在设定ArrayList指定位置的值只需要将旧值覆盖即可
public E set(int index, E element) { //index 设置值的下标 element 要设定的值
rangeCheck(index); //checkindex
E oldValue = elementData(index); //获取久值
elementData[index] = element; // 将element赋值给数组指定的下标
return oldValue; //返回旧值
}
从上面看来set方法结构很简单,checkindex,然后覆盖值
private void rangeCheck(int index) {
if (index >= size) //比较index与size的大小
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
获取指定位置的值方法
@SuppressWarnings("unchecked") //字符串{ @ code“unchecked”}被用来抑制未经检查的警告。
E elementData(int index) {
return (E) elementData[index];
}
3) add(int index, E element) 方法
在指定位置加值,调用ensureCapacityInternal(int minCapacity)确定数组大小,如果长度不够就扩容,然后新建一个新数组将旧数组
index之前的值以及index后的值复制到新数组,最后把参数element赋值给新数组的指定下标
public void add(int index, E element) {
rangeCheckForAdd(index); //checkindex,与set里面一致
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}