1、Collection
1.1、list集合
常用的实现类:
- Vector 是 Java 早期提供的线程安全的有序集合,如果不需要线程安全,不建议使用此集合,毕竟同步是有线程开销的。
- ArrayList 是最常见的非线程安全的有序集合,因为内部是数组存储的,所以随机访问效率很高,但非尾部的插入和删除性能较低,如果在中间插入元素,之后的所有元素都要后移。在多线程情况下可以使用Collections.synchronizedList()来代替。
- LinkedList 是使用双向链表数据结构实现的,因此增加和删除效率比较高,而随机访问效率较差。
1.2、set集合
-
HashSet 是一个没有重复元素的无序的集合。虽然它是 Set 集合的子类,实际却为HashMap 的实例,相关源码如下:
public HashSet() { map = new HashMap<>(); }HashSet 默认容量为 16,每次扩充 0.75 倍
-
LinkedHashSet 是按照元素的 hashCode 值来决定元素的存储位置,但同时又使用链表来维护元素的次序,这样使得它看起来像是按照插入顺序保存的。
-
TreeSet 集合实现了自动排序,也就是说 TreeSet 会把你插入数据进行自动排序。
2、集合排序
2.1、Comparable接口
如果一个类实现了Comparable 接口,就意味着该类有了排序功能。重写里面的compareTo(T o)方法。它是一个内部比较器。
2.2、Comparator接口
Comparator 是一个外部比较器,位于 java.util 包下,之所以说 Comparator 是一个外部比较器,是因为它无需在比较类中实现 Comparator 接口,而是要新创建一个比较器类来进行比较和排序。
3、Vector 和 ArrayList 初始化大小和容量扩充有什么区别?
Vector 和 ArrayList 的默认容量都为 10,Vector 容量扩充默认增加 1 倍,ArrayList 容量扩充默认增加大概 0.5 倍 (oldCapacity + (oldCapacity >> 1))。
4、Map
Map 常用的实现类如下:
- Hashtable:Java 早期提供的一个哈希表实现,它是线程安全的,不支持null 键和值,因为它的性能不如 ConcurrentHashMap,所以很少被推荐使用。
- HashMap:最常用的哈希表实现,如果程序中没有多线程的需求。HashMap 是一个很好的选择,支持 null 键和值,如果在多线程中可用ConcurrentHashMap 替代。
- TreeMap:基于红黑树的一种提供顺序访问的 Map,自身实现了 key 的自然排序,也可以指定 Comparator 来自定义排序。
- LinkedHashMap:HashMap 的一个子类,保存了记录的插入顺序,可在遍历时保持与插入一样的顺序。 HashMap底层数据结构
- HashMap 底层的数据是数组被成为哈希桶,每个桶存放的是链表,链表中的每个节点,就是 HashMap 中的每个元素。在 JDK 8 当链表长度大于等于 8 时,就会转成红黑树的数据结构,以提升查询和插入的效率。
4.1、HashMap主要方法
1)添加方法:put(Object key, Object value)
执行流程如下:
- 对 key 进行 hash 操作,计算存储 index;
- 判断是否有哈希碰撞,如果没碰撞直接放到哈希桶里,如果有碰撞则以链表的形式存储;
- 判断已有元素的类型,决定是追加树还是追加链表,当链表大于等于 8时,把链表转换成红黑树;
- 如果节点已经存在就替换旧值;
- 判断是否超过阀值,如果超过就要扩容。
2)获取方法:get(Object key)
执行流程如下:
- 首先比对首节点,如果首节点的 hash 值和 key 的 hash 值相同,并且首节点的键对象和 key 相同(地址相同或 equals 相等),则返回该节点;
- 如果首节点比对不相同、那么看看是否存在下一个节点,如果存在的话,可以继续比对,如果不存在就意味着 key 没有匹配的键值对。
5、HashMap 和 Hashtable 有什么区别?
Hashtable 使用了 synchronized 关键字来保障线程安全,而 HashMap 是非线程安全的; HashMap 允许 K/V 都为 null,而 Hashtable K/V 都不允许 null;
6、什么是哈希冲突?有哪些方法可以解决哈希冲突?
当输入两个不同值,根据同一散列函数计算出相同的散列值的现象,我们就把它叫做碰撞(哈希碰撞)。
哈希冲突的常用解决方案:
开放定址法:当关键字的哈希地址 p=H(key)出现冲突时,以 p 为基础,产生另一个哈希地址 p1,如果 p1 仍然冲突,再以 p 为基础,产生另一个哈希地址 p2,循环此过程直到找出一个不冲突的哈希地址,将相应元素存入其中。
再哈希法:这种方法是同时构造多个不同的哈希函数,当哈希地址Hi=RH1(key)发生冲突时,再计算 Hi=RH2(key),循环此过程直到找到一个不冲突的哈希地址,这种方法唯一的缺点就是增加了计算时间。
7、HashMap 在 JDK 7 和 JDK 8 中有哪些不同?
- HashMap 在 JDK 7 和 JDK 8 的主要区别如下。存储结构:JDK 7 使用的是数组 + 链表;JDK 8 使用的是数组 + 链表 +红黑树。
- 存放数据的规则:JDK 7 无冲突时,存放数组;冲突时,存放链表;JDK 8在没有冲突的情况下直接存放数组,有冲突时,当链表长度小于 8 时,存放在单链表结构中,当链表长度大于 8 时,树化并存放至红黑树的数据结构中。
- 插入数据方式:JDK 7 使用的是头插法(先将原位置的数据移到后 1 位,再插入数据到该位置);JDK 8 使用的是尾插法(直接插入到链表尾部/红黑树)。
8、HashMap基础
HashMap继承了AbstractMap类,实现了Map,Cloneable,Serializable接口。 HashMap 有两个重要的参数:容量(Capacity)和负载因子(LoadFactor)。
- 容量(Capacity):是指 HashMap 中桶的数量,默认的初始值为 16。
- 负载因子(LoadFactor):也被称为装载因子,LoadFactor 是用来判定HashMap 是否扩容的依据,默认值为 0.75f,装载因子的计算公式 = HashMap 存放的 KV 总和(size)/ Capacity。