数组和链表,应该算是最基本的数据结构了吧。
最近在帮公司招人,这个问题我几乎是必问的,然而还是有很多同学答不上来呀,不禁让人唏嘘感慨,基础知识大家还是要多补补哦!
长度一百万的数组,get(0)和get(999999)性能有区别吗?
回答这个问题之前,先来看看数组和链表的一个区别,这样再回过头来看答案时,就会很清晰。
数组
数组(Array)是有序的元素序列。 若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。 这些有序排列的同类数据元素的集合称为数组。
数组是一种最常用的线性数据结构,它的特点是内存连续,随机访问速度极快。
为什么数组随机访问速度极快?
数组的随机访问速度快,这主要依赖于连续的物理内存。因为内存是连续的,且每个元素占用的空间是等长的,所以不管数组多长,不管访问哪个元素,时间复杂度都是O(1),整个访问过程都只需要两次寻址。
数组的随机访问速度快,但是元素的插入和删除效率较低。
使用ArrayList,插入元素时,插入下标后的所有元素均需要往后移动一位。删除元素时,删除下标后的所有元素均需要向前移动一位。
链表
链表又细分为单向链表、双向链表、循环链表。
单向链表
每个节点维护一个额外的next属性,指向下一个节点。
双向链表
每个节点维护两个额外的属性prev和next,分别指向上一个节点和下一个节点。
循环链表
和双向链表一样,区别是头尾节点是相连的。
链表的优点就是数据的删除、添加速度很快,元素不用移动位置,只需要修改头尾指针的指向就可以了。
缺点是随机访问性能很差,为什么这么说呢?
当使用链表去访问下标数据时,流程大致如下,伪代码:
class Linked{
Object data;
Linked prev;
Linked next;
Object get(int index){
Linked node = this;
while (index > 0) {
node = this.next;
index--;
}
return node.data;
}
}
链表没有下标,不像数组可以根据下标+步长直接算出元素的内存地址。链表需要通过访问next节点,不断的寻址,一个一个往下找,时间复杂度是O(n)。
数组和链表的性能对比
理论讲完了,该实战一下这两者的性能差距到底几何。
分别创建一百万长度的数组和链表,访问1万次最后的元素:
class Test{
static int len = 1000000;
public static void main(String[] args) {
array();
linked();
}
static void array(){
List list = new ArrayList(len);
for (int i = 0; i < len; i++) {
list.add(i);
}
long t1 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
list.get(len-1);
}
long t2 = System.currentTimeMillis();
System.out.println("数组耗时:"+(t2 - t1));
}
static void linked(){
List list = new LinkedList();
for (int i = 0; i < len; i++) {
list.add(i);
}
long t1 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
list.get(len-1);
}
long t2 = System.currentTimeMillis();
System.out.println("链表耗时:"+(t2 - t1));
}
}
控制台输出:
What???说好的数组随机访问速度极快呢???怎么没体现出来???
哈哈,别着急,速度没区别是因为JDK对LinkedList.get()
方法做了一些优化。
单纯的访问最后一个元素,数组和链表几乎没区别,都是两次寻址就能定位到元素。
知道原因后,代码改起来就简单了,访问中间的元素不就可以了嘛。
将list.get(len-1)
改为list.get(len/2)
,代码就不重复贴了,再次测试,结果如下:
看到没,效果是不是非常明显,数组耗时1毫秒,而链表用了整整23秒多,碾压式的胜利啊。
而且,链表的耗时会随着List长度的增加而增加,数组不管长度多少,性能都不会波动,原因前面已经说过了。
尾巴
长度一百万的数组,get(0)和get(999999)性能有区别吗?
相信通过这篇文章,我不说答案,大家也都知道了!!!
由于数组的内存是连续的,当需要创建长度很大的集合时,JVM很可能由于找不到一块很大的连续内存而导致内存溢出,这时可以考虑使用链表。
你可能感兴趣的文章: