剖析面试最常见问题之 Java 集合框架
集合概述
说说 List,Set,Map 三者的区别?
List
(
对付顺序的好帮手
)
: 存储的元素是有序的、可重复的。
Set
(
注重独一无二的性质
):
存储的元素是无序的、不可重复的。
Map
(
用
Key
来搜索的专家
):
使用键值对(
key-value
)存储,类似于数学上的函数
y=f(x)
,
“x”
代表
key
,
"y"
代表
value
,
Key
是无序的、不可重复的,
value
是无序的、可重复的,每个键最多映射
到一个值。
集合框架底层数据结构总结
先来看一下 Collection 接口下面的集合。
List
Arraylist
:
Object[]
数组
Vector
:
Object[]
数组
LinkedList
: 双向链表
(JDK1.6
之前为循环链表,
JDK1.7
取消了循环
)
Set
HashSet
(无序,唯一)
:
基于
HashMap
实现的,底层采用
HashMap
来保存元素
LinkedHashSet
:
LinkedHashSet
是
HashSet
的子类,并且其内部是通过
LinkedHashMap
来
实现的。有点类似于我们之前说的
LinkedHashMap
其内部是基于
HashMap
实现一样,不过还是
有一点点区别的
TreeSet
(有序,唯一): 红黑树
(
自平衡的排序二叉树
)
Map
HashMap
:
JDK1.8
之前
HashMap
由数组
+
链表组成的,数组是
HashMap
的主体,链表则是主要
为了解决哈希冲突而存在的(
“
拉链法
”
解决冲突)。
JDK1.8
以后在解决哈希冲突时有了较大的变
化,当链表长度大于阈值(默认为
8
)(将链表转换成红黑树前会判断,如果当前数组的长度小于
64
,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时
间
LinkedHashMap
:
LinkedHashMap
继承自
HashMap
,所以它的底层仍然是基于拉链式散列结构
即由数组和链表或红黑树组成。另外,
LinkedHashMap
在上面结构的基础上,增加了一条双向链
表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺
序相关逻辑。
Hashtable
: 数组
+
链表组成的,数组是
Hashtable
的主体,链表则是主要为了解决哈希冲突而
存在的
TreeMap
: 红黑树(自平衡的排序二叉树)。
如何选用集合?
主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用
Map
接口下的集合,需要排 序时选择 TreeMap
,
不需要排序时就选择
HashMap
,
需要保证线程安全就选用
ConcurrentHashMap
。
当我们只需要存放元素值时,就选择实现
Collection
接口的集合,需要保证元素唯一时选择实现
Set 接口的集合比如 TreeSet
或
HashSet
,不需要就选择实现
List
接口的比如
ArrayList
或
LinkedList
,然后再根据实现这些接口的集合的特点来选用。
Collection 子接口之 List
Arraylist 和 Vector 的区别?
ArrayList
是
List
的主要实现类,底层使用
Object[ ]
存储,适用于频繁的查找工作,线程 不安全 ;
Vector
是
List
的古老实现类,底层使用
Object[ ]
存储,线程安全的。
Arraylist 与 LinkedList 区别?
1.
是否保证线程安全:
ArrayList
和
LinkedList
都是不同步的,也就是不保证线程安全;
2.
底层数据结构:
Arraylist
底层使用的是
Object
数组
;
LinkedList
底层使用的是
双向链表
数据结构(
JDK1.6
之前为循环链表,
JDK1.7
取消了循环。注意双向链表和双向循环链表的区别,
)
3.
插入和删除是否受元素位置的影响:
ArrayList
采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执
行
add(E e)
方法的时候,
ArrayList
会默认在将指定的元素追加到此列表的末尾,这种情
况时间复杂度就是
O(1)
。但是如果要在指定位置
i
插入和删除元素的话(
add(int index, E
element)
)时间复杂度就为
O(n-i)
。因为在进行上述操作的时候集合中第
i
和第
i
个元素之后
的
(n-i)
个元素都要执行向后位
/
向前移一位的操作。
LinkedList
采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响
(
add(E e)
、
addFirst(E e)
、
addLast(E e)
、
removeFirst()
、
removeLast()
),近似
O(1)
,如果是要在指定位置
i
插入和删除元素的话(
add(int
index, E element)
,
remove(Object o)
) 时间复杂度近似为
O(n)
,因为需要先移动到
指定位置再插入。
4.
是否支持快速随机访问:
LinkedList
不支持高效的随机元素访问,而
ArrayList
支持。快速随
机访问就是通过元素的序号快速获取元素对象
(
对应于
get(int index)
方法
)
。
5.
内存空间占用:
ArrayList
的空 间浪费主要体现在在
list
列表的结尾会预留一定的容量空间,而
LinkedList
的空间花费则体现在它的每一个元素都需要消耗比
ArrayList
更多的空间(因为要存放
直接后继和直接前驱以及数据)。
补充内容
:
双向链表和双向循环链表
双向链表:
包含两个指针,一个
prev
指向前一个节点,一个
next
指向后一个节点。
双向循环链表:
最后一个节点的
next
指向
head
,而
head
的
prev
指向最后一个节点,构成一个环。
Collection 子接口之 Set
comparable 和 Comparator 的区别
comparable
接口实际上是出自
java.lang
包 它有一个
compareTo(Object obj)
方法用来排序
comparator
接口实际上是出自
java.util
包它有一个
compare(Object obj1, Object obj2)
方
法用来排序
一般我们需要对一个集合使用自定义排序时,我们就要重写
compareTo()
方法或
compare()
方法,当 我们需要对某一个集合实现两种排序方式,比如一个 song
对象中的歌名和歌手名分别采用一种排序方 法的话,我们可以重写 compareTo()
方法和使用自制的
Comparator
方法或者以两个
Comparator
来实 现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 Collections.sort()
.
Comparator 定制排序
Output:
重写 compareTo 方法实现按年龄来排序
无序性和不可重复性的含义是什么
1
、什么是无序性?无序性不等于随机性 ,无序性是指存储的数据在底层数组中并非按照数组索引的顺
序添加 ,而是根据数据的哈希值决定的。
2
、什么是不可重复性?不可重复性是指添加的元素按照
equals()
判断时 ,返回
false
,需要同时重写 equals()方法和
HashCode()
方法。
比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
HashSet
是
Set
接口的主要实现类 ,
HashSet
的底层是
HashMap
,线程不安全的,可以存储
null 值;
LinkedHashSet
是
HashSet
的子类,能够按照添加的顺序遍历;
TreeSet
底层使用红黑树,能够按照添加元素的顺序进行遍历,排序的方式有自然排序和定制排序。