各位小伙伴,今天介绍哈ArrayList,Vector以及LinkedList的区别及其原理实现,JDK版本1.8;
介绍:
1.ArrayList继承AbstractList,实现List,其List的子类
2.Vector继承AbstractList,实现List,其List的子类
3.LinkedList继承AbstractSequentialList,实现List,其List的子类,特别说明下也实现了其Deque,且Deque继承了Queue(队列)
说明:
1.ArrayList底层实现原理是Object[],是以数组形式实现的;
声明其数组类型的变量elementData,至于transient关键字(序列化与反序列化,针对属性值的安全),大家有兴趣的朋友可以深入了解下.
在添加的方法中,很明显的看其在添加的时候就是在添加当elementData数组中.在添加中最重要的使用原理:
ArrayList在add方法中返回的数组是 从一个旧的数组copy到一个新的数组是根据适应性扩容数得到一个新的数组,所以在这方面是非常的耗时;
在我们常用的get方法中,就是根据索引index进行获取数组elementData当中的值.
2.Vector底层原理实现跟ArrayList实现是一样的Object[],都是数组:
声明其数组elementData属性;
在添加的方法中,很明显的看其在添加的时候就是在添加当elementData数组中,那么比ArrayList多的就是在方法上我们看见了我们熟悉的synchronized关键字(同步锁),看到这个关键字大家心里就明白了为什么Vector线程安全了吧!
备注:同步锁的使用,简单的介绍哈,如上面在add方法上加synchronized,那么在2个线程同时执行add方法时,第一个线程先执行完add方法的话,第二个线程是阻塞的,必须要等待第一个线程执行完才能轮到第二个线程执行add方法.
其它同ArrayList同理,在差距上就在方法上了多一个synchroized关键字.
3.Linkedlist底层实现原理是链表(双向链表) Node<E>(双向节点),
查看LinkedList有三个成员变量:
transient int size 集合数量; transient Node<E> first 第一个节点对象; transient Node<E> last 最后一个节点对象; 其中是根据transient进行声明的,在前面ArrayList当中也用到过,transient(序列号与反序列化,针对属性值安全)
Node对象: E item 在add方法时候,存放的对象; Node<E> prev 上一个节点对象; Node<E> next 下一个节点对象
通过介绍了LinkedList双向链表实现的几个比较重要的变量,那么我们进入到LinkedList,解谜LinkedList的链表实现,首先看其比较常用的的方法:
在添加的方法中,我们看到有了add方法执行了LinkLast方法,在有图中,我们看到了其LinkLast方法,我们来解析下linkLast方法:
LinkedList<Integer> list=new LinkedList<Integer>();
list.add(10000); 进入到linkLast(E e)方法,示例进入方法,进入方法说明
final Node<E> l = null; //第一次last变量为null;
final Node<E> newNode = new Node<>(l,10000,null); //进入到Node对象赋值,prev为null;element为10000;next为null
last = newNode;//赋值last为最后一个对象为newNode,目前只添加了一个,那么最后一个节点对象肯定就是为10000的节点对象if(l == null){//第一次执行add方法时,l为null,就赋值first为10000的对象
first = newNode;
}else{//当第二次或者第三次或者更多执行add方法时,l.next即为赋值下一个对象next;
l.next = newNode;
}
size++;//linkedlist的数量为1
modeCount++;
list.add(20000); 第二次进入到linkLast(E e)方法,示例进入方法,进入方法说明
final Node<E> l = last; //第二次last变量为10000的Node对象;
final Node<E> newNode = new Node<>(l,20000,null); //进入到Node对象赋值,prev为10000的Node对象;element为20000;next为null
last = newNode; //赋值last为最后一个对象为newNode,又添加了一个,那么最后一个节点对象肯定就是为20000的节点对象if(l == null){ //第一次执行add方法时,l为null,就赋值first为10000的对象
first = newNode;
}else{ //当第二次或者第三次或者更多执行add方法时,last为10000的节点对象,l.next即为赋值下一个对象next;那么这个20000的newNode对象进入到该方法,
l.next = newNode;l.next就为20000的对象,就会赋值10000的Node对象的next为20000的对象
}
size++;//linkedlist的数量为2
modeCount++;
list.add(30000); 第二次进入到linkLast(E e)方法,示例进入方法,进入方法说明
final Node<E> l = last; //第二次last变量为20000的Node对象;
final Node<E> newNode = new Node<>(l,30000,null); //进入到Node对象赋值,prev为20000的Node对象;element为30000;next为null
last = newNode; //赋值last为最后一个对象为newNode,又添加了一个,那么最后一个节点对象肯定就是为30000的节点对象if(l == null){ //第一次执行add方法时,l为null,就赋值first为10000的对象
first = newNode;
}else{ //当第二次或者第三次或者更多执行add方法时,last为20000的节点对象,l.next即为赋值下一个对象next;那么这个30000的newNode对象进入到该方法,
l.next = newNode;l.next就为30000的对象,就会赋值20000的Node对象的next为30000的对象
}
size++;//linkedlist的数量为3
modeCount++;
根据代码实例分析,很明显的看到Linkedlist集合对象的底层实现就是个双向链表,只要知道first Node对象以及last Node对象,我们就能查找到所有对象;
first Node<E> prev为null;elment为10000;next为20000的Node对象;
last Node<E> prev为20000的对象;element为30000;next为null;
通过以上得知,我们在LinkedList添加的时候,效率是非常高的,直接通过赋值next或者prev就能直接添加指定职位;
下面我通过一个草图来表达下这个LinkedList对象:
根据上面的草图,大家一看就明了,看着就像一个树形结构一样,next指定下一个节点对象,prev指定上一个节点对象,一层一层互相关联;所以通过上面的结构我们很明显的知道了Linkedlist查找起来就非常的痛苦,需要一层层的查找;所以效率方面就非常的低;在LinkedList里面如果要查找指定的某个节点,LinkedList做了一些小小的优化,就是单单的减少了查找的次数而已,但是效率还是非常的低,在linkedlist.size()比较大的情况下,查看源码:
在上面我们看到了,通过index,先进行判断index是否小于数量/2,如果小于,就从头部开始查找,如果大于数量/2,那么就从尾部开始查找,这就是为什么LinkedList查找我们需要的节点时慢了.而ArrayList,Vector查找就比较快,通过index直接获取数组指定的值;
我们基本简单源码分析完了ArrayList,Vector,LinkedList;进行相应的总结:
1.ArrayList 数组 查找效率高 添加效率低 线程不安全 有序
2.Vertor 数组 查找效率高 添加效率低 线程安全 有序
3.LinkedList 链表 查找效率低 添加效率高 线程不安全 有序
还有一种保证线程安全的写法:
Collections.synchroizedList(new ArrayList());
Collections.synchroizedList(new LinkedList());
总体完毕,谢谢大家!