集合框架知识大全
常用的集合类
Map和Collection接口是所有集合框架的父接口:
-
Collection接口的子接口包括Set接口和List接口
-
Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
-
Set接口的主要实现类主要有:HashSet、TreeSet、LinkedHashSet等
-
List接口的主要实现类有:ArrayList、LinkedList、Stack以及Vector等
Map、Set、List三者的区别
Collection包括List、Set两大接口
-
List:一个有序(元素存入集合的顺序和取出的顺序一样)容器,元素可以重复,可以存入多个null元素,元素有索引。
-
Set:一个无序(存入和取出顺序可能不一样)容器,元素不能重复,只能存入一个null元素,元素必须保证唯一性。
-
Map:是一个键值对集合,存储键、值之间的映射。Key:无序,且唯一。value:不要求有序,允许重复。Map没有继承Collection,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
集合框架底层数据结构
Collection
-
List
-
ArrayList: Object数组
-
Vector: Object数组
-
LinkedList: 双向循环链表
-
-
Set
-
HashSet(无序,唯一):基于HashMap实现的,底层采用的HashMap保存元素。
-
LinkedHashSet:LinkedHashSet继承于HashSet,并且内部是通过LinkedHashMap来实现的。有点类似与LinkeddHashMap其内部是基于HashMap实现的一样,不过还是有一点点区别。
-
Map
-
HashMap:
JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的("拉链法")。JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索的时间。
-
LinkedHashMap:
LinkedHashMap继承自HashMap,所以它的底层仍然时基于拉链式散列结构即由数组和链表或红黑树组成的。另外,LinkedHashMap在此结构上增加了一条双向链表,使上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序的相关逻辑。
-
HashTable
数组+链表组成,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的
-
TreeMap
红黑树(自平衡的排序二叉树)
线程安全的集合类
-
Vector
就比ArrayList多了个同步化机制(线程安全),因为效率低,不建议使用。
-
stack
堆栈类,继承自Vector,先进后出。
-
hashtable
就比HashMap多了个线程安全。
-
enumeration
枚举,相当于迭代器。(定义后第一次真正使用会被虚拟机加载并初始化,这个初始化过程是线程安全的)
ArrayList的优缺点
优点:
-
ArrayList底层是数组,是一种随机的访问模式。ArrayList实现了RandomAccess接口,因此查找的时候很快。
-
ArrayList在顺序添加一个元素很快。
缺点:
-
删除和插入元素的时候,需要做一次元素的复制操作,如果元素很多,就会比较耗费性能。
ArrayList比较适合顺序添加、随机访问的场景。
ArrayList和LinkedList的区别
-
数据结构实现:ArrayList和Vector都是动态数组实现的,而LinkedList是由双向链表的数据结构实现的。
-
随机访问效率:ArrayList比LinkedList在随机访问的时候效率比较高,因为LinkedList是线性的数据存储方式,所以移动指针从前往后一次查找。
-
增加和删除效率:在非首尾的增加和删除操作,LinkedList要比ArrayList效率高,因为ArrayList增删操作要影响数组内其他数据的下标。
-
内存空间占用:LinkedList比ArrayList更占内存,因LinkedList的节点除了存储数据,还存储了两个引用,一个提前指向前一个元素,一个指向后一个元素。
-
线程安全:ArrayList和LinkedList都是不同步的,也就是不保证线程安全的。
综合来说,在需要频繁读取集合中元素时,更推荐ArrayList,而在插入和删除操作较多的时候,更推荐LinkedList。
ArrayList和Vector的区别
这两个类都实现了List接口,他们都是有序的集合。
-
线程安全:Vector使用了Synchronized来实现线程同步的,是线程安全的,而ArrayList是非线程安全的。
-
性能:ArrayList在性能方面要优于Vector。
-
扩容:ArrayList和Vector都会根据实际的需要动态的调整容量,只不过在Vector扩容每次都会增加1倍,而ArrayList只会增加50%。
HashSet的实现原理
HashSet是基于HashMap实现的,HashSet的值存放在HashMap的key上,HashMap的value统一为present,因此HashSet的实现比较简单,相关的HashSet操作,基本上的都是调用底层HashMap的相关方法来完成,HashSet不允许重复的值。
HashMap的实现原理
HashMap概述:HashMap是基于哈希表的Map接口而非同步实现。此实现提供可选的映射操作,并允许使用null值和null键,此类不保证映射的顺序。
HashMap的数据结构:HashMap实际上是一个"链表散列"的数据结构,即数组和链表的结合体。
hashmap是基于hash算法实现的
-
当我们往hashmap中put元素时,利用key的hashcode重新hash计算出当前对象的元素在数组中的下标。
-
存储时,如果存在相同hash值的key,此时会有两种情况。(1)如果key相同,则会覆盖原始的值(2)如果key不同(出现冲突),则将当前的key-value放入链表中。
-
获取时,直接找到hash值对应的下标,在进一步的判断key是否相同,从而找到对应的值。
JDK1.8中对HashMap进行了优化,当链表中的节点数据超过8个之后,该链表会转化为红黑树来提高查询效率,从原来的O(n)到O(logn)
JDK1.8以前采用的拉链法。拉链法:将链表和数组相结合。数组中每一格就是一个链表。若遇到hash冲突,则将冲突的值加到链表中即可。
JDK1.8之后,当链表的长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索的时间。
HashMap1.7和1.8之间的区别
HashSet和HashMap的区别
| HashMap | HashSet |
|---|---|
| HashMap实现了Map接口 | HashSet实现了Set接口 |
| HashMap储存键值对 | HashSet仅仅存储键对象 |
| 使用put()方法将元素放入map中 | 使用add()方法将元素放入set中 |
| HashMap中使用键对象来计算hashcode值 | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false |
| HashMap比较快,因为是使用唯一的键来获取对象 | HashSet较HashMap来说比较慢 |
HashMap和HashTable的区别
-
线程安全:HashTable内部的方法基本都见过synchronized修饰,所以hashtable是线程安全,hashmap是非线程安全。(如果要保证线程安全,使用ConcurrentHashMap)
-
效率:因为线程安全,HashMap比hashtable效率高。(hashtable基本不使用)
-
对于null的支持:HashMap中,null可以作为键,这样的键只能有一个,可以有一个或者多个键所对应的值为null。但是在HashTable中put的值只要是null就会抛出空指针异常。
-
扩容:
(1)创建时如果不指定容量初始值,Hashtable默认的初始大小为11,之后每次扩容,容量变为原来的2n+1。Hashtable默认为16,之后每次扩容,容量都会变成原来的2倍。
(2)指定了大小时,HashTable会直接使用指定的大小。而HashMap会将其扩充为2的幂次方大小,也就是说HashMap总会使用2的幂作为哈希表的大小。
-
底层数据结构:主要体现在JDK1.8以后,hashmap采用了数组+链表+红黑树。而hashtable没有这样的机制。