您现在的位置是:首页 >技术交流 >集合框架底层数据结构网站首页技术交流
集合框架底层数据结构
Collection
1. List
Arraylist : Object 数组
Vector : Object 数组
LinkedList : 双向循环链表
2.Set
HashSet (无序,唯一) : 基于HashMap 实现的,底层采用HashMap 来保存元素
LinkedHashSet : LinkedHashSet 继承于 HashSet , 并且其内部是通过LinkedHashMap
其内部是基于HashMap 实现一样,不过还是有一点点区别的
TreeSet (有序,唯一) : 红黑树(自平衡的排序二叉树)
Map
- HashMap : JDK1.8之前HashMap 由数组+链表组成的,数组是HashMap 主体,链表则是主要为了解决哈希冲突而存在的("拉链法"解决冲突),JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阀值(默认为8)时,将链表转化为红黑树,以减少搜索时间
- LinkedHashMap : LinkedHashMap 继承于HashMap , 所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序,同时通过对链表进行相应的操作,实现了访问顺序相关逻辑
- HashTable : 数组+链表组成的,数组是HashMap 主体,链表则是主要为了解决哈希冲突而存在的
- TreeMap : 红黑树(自平衡的排序二叉树)
List 和Set 比较,各自的子类比较?
对比一:ArrayList 与 LinkedList 的比较
1、ArrayList 是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,
查询操作效率会比较高(在内存里是连着放的)
移动数据,所以插入和删除操作效率比较低
2、LinkedList 基于链表的数据结构,地址是任意的,
所以在开辟内存空间的时候不需要等一个连续的地址,
对于新增和删除操作add 和 remove,LinkedList 比较占优势
移动指针时,所以查询操作性能比较低
使用场景分析:
当需要对数据进行对此访问的情况下选用ArrayList ,当需要对数据进行多次增加删除修改时采用LinkedList
对比二:ArrayList 与 Vector 的比较
- Vector 的方法都是同步的,是线程安全的,而ArrayList 的方法不是,由于线程的同步必然是要影响性能,因此,ArrayList 的性能比Vector 好
- 当Vector 或 ArrayList 中的元素超过它的初始大小时,Vector 会将它的容量翻倍,而ArrayList 只增加50%的大小,这样,ArrayList 就有利于节约内存空间
- 大多数情况不使用Vector ,因为性能不好,但是它支持线程的同步,即某一时刻只有一个线程能够写Vector ,避免多线程同时写而引起的不一致性
- Vector 可以设置增长因子,而ArrayList 不可以
使用场景分析:
- Vector 是线程同步的,所以它也是线程安全的,而ArrayList 是线程异步的,是不安全的,如果不考虑线程的安全因素,一般用ArrayList 效率比较高
- 如果集合中的元素数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector 有一定的优势
对比三:HashSet 与 TreeSet 的比较
- TreeSet 是二叉树实现的,TreeSet 中的数据是自动排好序的,不允许放入null值
- HashSet 是哈希表实现的,HashSet 中的数据是无序的,可以放入null ,但只能放入一个null ,两者中的值不能重复,就如数据库中唯一约束
- HashSet 要求放入的对象必须实现HashCode() 方法,放入的对象,是以Hashcode码作为标识的,而具有相同内容的String 对象,Hashcode是一样,所以放入内容不能重复,但是同一类的对象可以放入不同的实例
使用场景分析:
HashSet 是基于Hash 算法实现的,其性能通常都优于TreeSet ,我们通常都应该使用HashSet ,在我们需要排序的功能时,我们才使用TreeSet
HashSet 与 HashMap 的区别?
HashMap | HashSet |
---|---|
实现了Map 接口 | 实现Set 接口 |
存储键值对 | 仅存储对象 |
调用put () 向Map 中添加元素 | 调用add () 方法向Set 中添加元素 |
HashMap 使用键(Key)计算Hashcode | HashSet 使用成员对象来计算Hashcode 值,对于两个对象来说Hashcode 可能相同,所以equals () 方法用来判断对象的相等性,如果两个对象不同的话,那么返回false |
HashMap 相对于HashSet 较快,因为它是使用唯一的键获取对象 | HashSet 较 HashMap 来说比较慢 |
HashSet 如何检查重复?HashSet 是如何保证数据不可重复的?
- 向HashSet 中 add() 元素时,判断元素是否存在的依据,不仅要比较Hash 值,同时还要结合equals() 方法比较
- HashSet 中的add() 方法会使用HashMap 的put() 方法
- HashMap 的key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap 的key,并且在HashMap 中如果K/V 相同时,会用新的V覆盖掉旧的 V,然后返回旧的V。所以不会重复(HashMap 比较key 是否相等是线比较Hashcode 再比较equals)
- 以下是HashSet 部分源码:
private static final Object PRESENT = new Object(); private transient HashMap<E,Object> map; public HashSet(){ map = new HashMap<>(); } public boolean add(E e){ //调用HashMap 的put方法,PRESENT 是一个至始至终都相同的虚值 retrun map.put(e, PRESENT)==null; }
HashSet 的实现原理?
HashSet 是基于HashMap 实现的,HashSet 的值存放于HashMap 的key上,
HashMap 的value 统一为present ,因此 HashSet 的实现比较简单,
相关HashSet 的操作,基本上都是直接调用底层HashMap 的相关方法来完成,
HashSet 不允许重复的值
HashMap 具体如何实现的?
HashMap 底层是基于数组+链表实现的,通过添加键的Hashcode 与上数组的长度来得到这个元素在数组中的位置,
如果这个位置没有数据,那么就把这个数据当做第一个节点,
如果这个位置有了链表,那么在JDK1.7 的时候使用的是头插法,在JDK1.8 的时候使用尾插法。
HashMap 在JDK1.8 的版本中引入了红黑树结构做优化,
当链表元素个数大于等于8时,链表转换成树结构;
当链表元素个数小于等于6时,树结构还原成链表。
HashMap的扩容机制?
HashMap 底层是数组,在第一次put 的时候会初始化,发生第一次扩容到16,
它有一个负载因子是0.75,下一次扩容的时候就是当前数组大小*0.75,
扩大容量为原来的2倍。