Java集合面试题

本文详细探讨了Java集合中HashMap的实现与优化,包括JDK7与JDK8的区别、HashMap的存储结构、扩容机制、哈希冲突解决,以及线程安全问题。还涉及了HashMap与ConcurrentHashMap的区别,ConcurrentHashMap的实现方式,以及Collection与Collections、List、Set、Map接口的区别。此外,文章还讨论了ArrayList、LinkedList、Vector的区别,线程安全的List选择,以及不同类型的Map如HashMap、HashTable、LinkedHashMap和TreeMap的特点。最后,介绍了HashSet、TreeSet、LinkHashSet的差异,以及Comparable和Comparator接口的作用和区别。
摘要由CSDN通过智能技术生成


JAVA集合

一、JDK7和JDK8中的HashMap有什么区别

JDK7中的HashMap,是基于数组+链表来实现的,它的底层维护一个Entry数组。它会根据计算的hashCode将对应的KV键值对存储在该数组中,一旦发生hashCode冲突,那么就会将该KV键值对放到对应的已有元素的后面,此时便形成了一个链表式的存储结构。
JDK7中HashMap的实现方案有一个明显的缺点,即当Hash冲突严重时,在桶上形成的链表会变得越来越长,这样在查询时的效率就会越来越低,其时间复杂度为O(N)。
JDK8中的HashMap,是基于数组+链表+红黑树来实现的,它的底层维护一个Node数组。当链表的存储的数据个数大于等于8时候,不再采用链表存储,而采用了红黑树存储结构。这么做主要是在查询的时间复杂度上进行优化,链表为O(N),而红黑树一直是O(logN),可以大大的提高查找性能。

二、HashMap底层的实现原理

它基于hash算法,通过put方法和get方法存储和获取对象。
存储对象时,我们将KV传给put方法时,它调用K的hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Factor则resize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。
如果发生碰撞的时候,HashMap通过链表将产生碰撞冲突的元素组织起来。在Java8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。
put解析:
1、 如果HashMap为空,则进行初始化;
2、调用哈希函数对Key求Hash值,然后再计算下标。并查找所在链表。
3、 如果链表长度超过阈值(TREEIFY_THRESHOLD == 8),就把链表转换成红黑树。
4、如果结点的键已经存在就替换旧值。否则用头插法插入新结点。
5、如果桶满了(容量+加载因子),就需要resize(双倍扩容,保证2的n次幂)进行扩容,并且为了使结点均匀分散,应该重新分配结点位置if(++size>threshold)resize();

public V put(K key, V value) {
    
	if (table == EMPTY_TABLE) {
    
	// 1. HashMap 为空,则进行初始化 
		inflateTable(threshold); 
	}
	// 键为 null 单独处理 
	if (key == null) 
		return putForNullKey(value); 
	// 2. 调用哈希函数对key求hash值,并取模得桶下标 
	int hash = hash(key); 
	int i = indexFor(hash, table.length); 
	// 3. 先找出是否已经存在键为 key 的键值对 
	for (Entry<K,V> e = table[i]; e != null; e = e.next) {
    
	Object k; 
	// 注:散列类容器唯一性判断 
	if (e.hash == hash && ((k = e.key) == key || key.equals(k))){
   
	//如果存在的话就更新这个键值对的值为 value 
	V oldValue = e.value; 
	e.value = value; 
	e.recordAccess(this); 
	return oldValue; 
	} 
}
modCount++; 
// 4. 插入新键值对(如果碰撞了则头插法,没有碰撞则直接插在
 addEntry(hash, key, value, i);
 return null; 
 }
 /*
 HashMap 允许插入键为 null 的键值对。但是因为无法调用 null 的 hashCode() 方法,也就无 法确定该键值对的桶下标,只能通过强制指定一个桶下标来存放。HashMap 使用第 0 个桶存放键为 null 的键值对。 
private V putForNullKey(V value) { 
	for (Entry<K,V> e = table[0]; e != null; e = e.next) {
		if (e.key == null) { 
			V oldValue = e.value; 
			e.value = value; 
			e.recordAccess(this); 
			return oldValue; 
			} 
		}
		modCount++; 
		addEntry(0, null, value, 0); 
		return null; 
	}
	*/

get解析:
1、调用哈希函数对Key求Hash值,然后再计算Entry数组的索引i。
2、 遍历table[i]为头结点的链表/红黑树,如果发现有节点hash,key都相同节点,则取出该节点的值。

public V get
  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值