关系图:
结合上图,先来总结一下java数据结构中的线性表:
- 线性表里的元素是按线性(逻辑上的)排列的 线性表分为顺序表和链表两大类
- 顺序表中的数据元素存储连续,内存划分的区域也连续,java中的实现是ArrayList
- 链表在物理存储上可以以非连续、非顺序的方式存储,数据元素的逻辑顺序是通过链表中的引用来实现的
- 链表又有单向链表、循环链表、双向链表,LinkedList就是以双向链表的方式实现的
- 还有两种比较特殊的线性表:栈和队列
- 栈仅允许在线性表的尾部进行添加和删除操作,这一端被称为栈顶,另一端称为栈底。向一个栈添加新元素叫压栈,删除元素又称为出栈,Stack就是栈在Java中的实现。
- 队列只能从头部删除(取出)元素,从队尾添加元素,进行删除操作的端称为队头,Queue就是队列的体现:
Queue queue = new LinkedList();
可以根据下述关键点自行源码剖析。
一、ArrayList要点:
- 初始化(数组一但在堆内存中创建,长度是固定的):
不指定初始化数组长度(public ArrayList(){...}
):底层默认在jvm内存中开辟长度为10的数组
指定初始化数组长度(public ArrayList(int initialCapacity){...}
):开辟指定长度的数组 - 数组扩容
手动扩容:利用System.arraycopy(...)
方法
自动扩容:ArrayList在添加新元素时,如在add方法中:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 判断数组是否需要扩容!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == 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);
}
- 时间复杂度
主要针对与LinkedList的删除、增加、查询方法比较 - 删除(删除最后一个元素时,直接删除不会进行部分元素整体复制)
按照下标删除
按照指定元素删除:默认删除数组中与该指定对象具有相同引用的对象元素,否则对象需要重写equals方法 - 增加
直接添加(add(T e){...}
):,先判断数组是否扩容,然后在数组最后以为添加该元素
在指定位置添加(add(int position, T e){...}
):先进行指定下标位置之后(包括该指定下标位置)的所有元素全部后移一位,然后把该下标引用指向该元素 - 修改
set(int index, E element) {...}
修改指定下标位置(index)的元素
二、Vector要点:
Vector在实际开发中很少使用到,这是因为在所有关键性的操作上,方法前面都加了synchronized关键字,来保证线程的安全性,加锁和释放锁的这个过程,在系统中是有开销的,因此,在单线程的环境中,Vector效率要差很多。
从上图可以看到,二者均实现了List接口,但有一个经常被提交的问题就是Vector和ArrayList有什么区别:
- Vector是线程安全的,ArrayList不是线程安全的
- Vector在底层数组不够用时在原来的基础上扩展0.5倍,ArrayList扩展1倍
三、LinkedList要点:
- 新建一个LinkedList:
List<T> tList = new LinkedList<T>();
查看源码,构造函数为空 - 主要实现依赖自身的三个变量和一个静态内部类:
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
- 认识LinkedList,首先从add方法入手:
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
- 再者,删除:
按照指定元素进行删除:可以发现LinkedList中是可以放null的。从下边代码中的equals发现,如过remove方法的参数是一个new出来的对象(很少这样操作,除非有特殊需求),那么它要重写从Object类继承过来的equals方法
按照下标进行删除:public E remove(int index){......}
public boolean remove(Object o) { // 按照指定元素进行删除
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x); // ArrayList中这里是fastRemove()方法
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
E unlink(Node<E> x) { // 把Node从链表中移出
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
@Override
public boolean equals(Object obj) {//equals方法重写示例
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof Animal) {
Animal other = (Animal) obj;
return this.name.equals(other.name) && this.habit.equals(other.habit);
}
return false;
}
后续待更新……