手写jdk1.7的源码
package com.catt.map.map7;
import com.catt.map.MyMap;
/**
* @author :yjq
* @date :Created in 2022/12/15 16:10
* @description:
*/
public class MyHashMap7<K, V> implements MyMap<K, V> {
//默认容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 0001
//加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//最大的容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//设置的加载因子
float loadFactor = 0;
//实际实体
transient Entry<K, V>[] table = null;
//实际存储大小
int threshold;
transient int size;
transient int hashSeed = 0;
//初始化hashMap
public MyHashMap7() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
/**
* @param initialCapacity 实际容量
* @param loadFactor
*/
public MyHashMap7(int initialCapacity, float loadFactor) {
if (initialCapacity < 0) {
//设置初始化容量异常
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
}
//防止超出最大值
if (initialCapacity > MAXIMUM_CAPACITY) {
initialCapacity = MAXIMUM_CAPACITY;
}
if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
}
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();
}
//自定义方法
void init() {
}
@Override
public int size() {
return size;
}
@Override
public V get(K key) {
if (key == null) {
//去取第一个位置
return getForNullKey();
}
Entry<K, V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
//去第一个位置
private V getForNullKey() {
if (size == 0) {
return null;
}
for (Entry<K, V> e = table[0]; e != null; e = e.next) {
if (e.key == null) {
return e.value;
}
}
return null;
}
private Entry<K, V> getEntry(K key) {
if (size == 0) {
return null;
}
int hash = (key == null) ? 0 : hash(key);
for (Entry<K, V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
//hash值相等,且key相同
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
return e;
}
}
return null;
}
@Override
public V put(K key, V value) {
if (table == null) {
// 初始化hashTable扩容
inflateTable(threshold);
}
if (key == null) {
//第一个位置
return putForNullKey(value);
}
int hash = hash(key);
int i = indexFor(hash, table.length);
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))) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
addEntry(hash, key, value, i);
return null;
}
private void inflateTable(int toSize) {
// Find a power of 2 >= toSize
int capacity = roundUpToPowerOf2(toSize);
//实际容量
threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
table = new Entry[capacity];
//initHashSeedAsNeeded(capacity);
}
private static int roundUpToPowerOf2(int number) {
// assert number >= 0 : "number must be non-negative";
return number >= MAXIMUM_CAPACITY
? MAXIMUM_CAPACITY
: (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}
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;
return oldValue;
}
}
addEntry(0, null, value, 0);
return null;
}
private void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex);
}
private void createEntry(int hash, K key, V value, int bucketIndex) {
// 创建我们的Entry对象
Entry<K, V> e = this.table[bucketIndex];
this.table[bucketIndex] = new Entry(key, value, hash, e);
size++;
}
static int indexFor(int h, int length) {
// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
return h & (length - 1);
}
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
//重写实体方法
static final class Entry<K, V> implements MyMap.Entry<K, V> {
K key;// key
V value;// value
Entry next;
int hash;
public Entry(K key, V value, int hash, Entry<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
this.hash = hash;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
this.value = value;
return value;
}
}
}
1.7常见的问题
1、hashMap的默认初始化容量为多少?
16
hashMAp怎么样解决hashcode冲突的?
链表 。hashcode一样会放链表里面
2、实际容量为多少
3、hahsmap是否可以存放key为Null的空对象
存放第一个位置
4、HashMap底层如何实现减少index下标冲突问题
通过链表
5、Hashmap底层扩容存在什么问题
jdk7会导致死循环的问题,原因链表头插法赋值,在多线程情况下,导致
多线程下、扩容时
A.next=B
B.next=A
598行,
6、HashMap是否可以存键值对为自定义对象
7、HashMap加载因子为什么为0.75
加载因子越大,
8、jdk7的hashMap存在那些问题
1、线程不安全
2、链表过长导致查询效率过低
3、底层扩容可能存在死循环问题
jdk8的hash冲突》jdk7的
9、hashMap的put方法是如何实现的
根据key计算hash值,使用hash值获取index位置
10、HashMap里面的index冲突和hash冲突的区别
index冲突原因:&length-1必须是偶数(链表的下标)
hash冲突原因:对象不同,hashcode相同,采用equals比较
11、hashMAp的链表是单向还是双向?
单向
12、HashMAp的链表过长存在什么问题
查询效率表低,时间复杂度O(n)
13、jdk7de HashMap根据key查询值时时间复杂度为多少
O(1) 数组值
1、没有hashCode,O(1)
2、有hashCode冲突时,查询就非常慢了