目录
链表的随机查找效率很低的,因为需要遍历一半的链表O(n/2)
5.1. LinkedList插⼊快? ArrayList, LinkedList 随机插⼊,头尾插⼊效率⽐较?
1.集合框架
Collection接口是所有集合的根,然后扩展开提供了三大类集合,分别是:
- List,也就是我们前面介绍最多的有序集合,它提供了方便的访问、插入、删除等操作
- Set,是不允许有重复元素的,这是和List最明显的区别,也就是不存在两个对象equals返回true.我们在日常开发中有很多需要保证元素唯一性的场合。
- Queue/Deque,则是Java提供的标准队列结构的实现,除了集合的基本功能,它还支持类似先进先出的或者后入先出等行为。
2.Collection
首先ArrayList、Vector、LinkedList集合都是继承了AbstractList抽象类。
ArrayList和Vector使用了数组实现,这两者的实现原理差不多。Vector是线程安全的,ArrayList不是线程安全的,LinkedList使用了双向链表实现。
2.1 ArrayList
2.1.1 基本结构
ArrayList实现了List接口,继承了AbstractList抽象类,底层是数组实现的,并且实现了自增扩容数组的大小。实现了RandomAccess意味着可以实现随机访问
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
默认的容量是10,。存数据的是transient Object[] elementData;注意这里的Object数组不是基本类型
为什么使用transient修饰数组?
如果采⽤外部序列化法实现数组的序列化,会序列化整个数组.内部提供了两个私有⽅法 writeObject 以及 readObject 来⾃我完成序列化与反序列化,从⽽在序列化与反序列化数组时节省了空间和时间。
2.1.2 add方法
先确认数组长度,再添加元素
尾插的时候在数组尾部增加元素,性能是非常好的,时间复杂度为0(1)
每次扩容都要按照1.5倍的大小进行扩容,科学计算,避免浪费更多的空间。
2.2 LinkedList
2.2.1 基本结构
由双向链表组成。每个链表中的元素都是一个Node节点对象,它包含了前指针和后继指针,还有鵆数据的item三部分组成
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;
}
}
2.2.2 add方法
尾插法:创建一个Node对象插入链表尾部时间复杂度为O(1)
随机插⼊: node(index) ⼆分思想判断从前向后遍历还是从后向前。时间复杂度O(n/2)
2.2.3 get方法
链表的随机查找效率很低的,因为需要遍历一半的链表O(n/2)
3.Stack栈
栈和队列都是为了实现某种需求⼀种逻辑结构。都可以使⽤数组/链表实现。
栈:先⼊后出,⼊栈出栈时间复杂度都为O(1)
应用:
栈的运⽤⼗分之⼴。函数调⽤栈,操作数栈,浏览器中的后退/前进,成对问题可以使⽤栈解决
4.Queue队列
概念: 先进先出,⼊队出队时间复杂度都为O(1)
队列这种数据结构既可以⽤数组来实现,也可以⽤链表来实现
应⽤场景:
等待排队的场景,阻塞队列⽤作⽣产消费模型
5.1. LinkedList插⼊快? ArrayList, LinkedList 随机插⼊,头尾插⼊效率⽐较?
ArrayList 随机(头插)插入的效率O(n)
ArrayList 尾插的效率O(1)
ArrayList get获取数据的时间复杂度O(1)
LinkedList默认尾插 时间复杂度为O(1)
LinkedList随机插入 时间复杂度为O(n/2)
LinkedList 获取数据 时间复杂度为O(n/2)二分查找的效率,需要遍历一半的链表
2.Map
2.1Map继承结构
Map接⼝作为Java中哈希表的顶级接⼝和集合Collection接⼝⽆关系
- HashTable是性能比较差的哈希表,所有方法都是用了Synchronozed描述
- TreeMap可排序的Map,底层使用红黑树实现。排序场景下考虑使用
- HashMap 存储K-V键值对结构,近似O(1)的时间复杂度快速通过Key取value。
- LinkedHashMap继承⾃ HashMap+⾃身维护了双向链表实现。
2.2JDK1.7版本
1.7的HashMap使用数组+链表的形式存储
Entry:每个节点都是一个Entry的实例,Entry包含了四个属性:key,value,hash,next
capacity:当前数组的容量,始终保持着2^n,可以扩容,扩容后数组大小为当前的两倍
loadFactor:负载因子,默认为0.75
threshold:扩容的阈值,等于capacity * loadFactor
2.3 JDK1.8
JDK1.8对HashMap进⾏了不少修改,最⼤的不同就是使⽤ 红⿊树 优化链表。所以由数组+链表+红⿊树组成。根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表⼀个个⽐较下去才能找到我们需要的,时间复杂度取决于链表的⻓度,为 O(n)。 此时哈希表退化成了链表为了降低这部分的开销,在 Java8 中,当链表中的 元素超过了 8 且数组⻓度⼤于64个以后,会将链表转换为红⿊树 ,在这些位置进⾏查找的时候可以降低时间复杂度为 O(logN)。
Java7 中使⽤ Entry 来代表每个 HashMap 中的数据节点, Java8 中使⽤ Node,基本没有区别,都是key, value, hash 和 next 这四个属性,不过, Node 只能⽤于链表的情况,红⿊树的情况需要使⽤TreeNode
3.Set
set是无序不重复。其实现是基于Map进⾏了⼀些封装,代码极少。