Array和ArrayList有什么区别?
- Array大小是固定的,空间不够时也不能再次赛区,所以需要事前确定适合的空间大小;而ArrayList大小是动态变化的,它可以动态增长,如果空间不够,它会创建一个空间比原空间大约0.5倍的新数组,然后将所有元素复制到新数组中,接着抛弃旧数组,而且,每次添加新的元素都会检查内部数组的空间是否足够。
- Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
- ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
- Array数组存放的时候一定是同种类型的元素,ArrayList就不一定了,因为ArrayList可以存储Object。
ArrayList和LinkedList有什么区别?
- ArrayList是实现了基于动态Object数组的数据结构,LinkedList基于链表(双向链表)的数据结构。
- 插入和删除是否受元素位置的影响: ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素 位置的影响。 比如:执行 add(E e) 方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话( add(int index, E element) )时 间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是 近似 O(1)而数组为近似 O(n)。
- ArrayList的底层是数组,它可以O(1)时间复杂度对元素进行随机访问;LinkedList是基于链表实现,查找元素的时间复杂度为O(n)。
- LinkedList的插入,添加,删除操作速度更快,同时其内存消耗比ArrayList更多,因为LinkedList为每一个节点存储了两个引用。
- ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全。
LinkedList链表优化
LinkedListList接口的双向链表实现。由于是链表结构,所以链表长度没有限制;添加、删除元素只需改变指针的指向即可,而ArrayList需要重整数组,所以LinkedList适用于添加、删除操作频繁的情况。
在JDK1.7前,LinkedList是通过headerEntry实现一个循环链表。先初始化一个空的Entry,用来做header,然后首尾相连,形成一个循环链表。
Node定义:
public class Node {
public String content;
public Node next;
public Node previous;
public Node(String content, Node next, Node previous) {
this.content = content;
this.next = next;
this.previous = previous;
}
}
循环链表实现:
public class ListNode_1 {
public Node header = new Node(null, null, null);
public int size = 0;
//添加元素节点
public void addNode(String content) {
Node newNode = new Node(content, header, header.previous);
header.previous.next = newNode;
header.previous = newNode;
size++;
}
public ListNode_1() {
header.previous = header;
header.next = header;
}
}
在JDK1.7中headerEntry循环链表被替换成了first和last组成的非循环链表:
public class ListNode_2 {
public Node first;
public Node last;
public int size = 0;
public void addNode(String content) {
Node index = last;
Node newNode = new Node(content, null, index);
last = newNode;
if (first == null) {
first = newNode;
}else {
index.next = newNode;
}
size++;
}
}
JDK1.7中的first/last对比以前的header有以下好处:
- JDK1.7有更清晰的链头和链尾概念,代码容易理解明白。
- JDK1.7能节省new一个Header(实例化headerEntry是为了让后面的方法更加统一,否则会有许多Header的空校验)。
- 在链头尾进行插入/删除操作,JDK1.7方式更加快捷。(插入/删除操作分为中间和两头两种情况:
在中间插入/删除,两者一样,都是先遍历找到index,然后修改index节点两边的指针(2个next,2个previou);在两头,则对于循环链表也是处理两边指针,而JDK1.7的非循环链表只需处理first或者last的一边指针(一个next,1个previous),所以理论上非循环链表更高效。实际操作中,链头和链尾操作最频繁。
对于遍历,两者都是链表指针循环,所以遍历效果一样。