简单来说,HashMap由数组+链表组成的(jdk1.8已经重构成,当链表长度为8时,退化为红黑树),数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。所以在设计HashMap时,引入了负载因子,引入的目的,我认为:1.减少Hash碰撞,2.提高寻址速度。
什么是哈希算法。我也不知道,很复杂。简单理解就是一个函数y=f(X),这个函数很奇怪,我们能只能通过X算出对应Y,但是不能通过Y来算出对应的X,举个命题:比如张三的生日是2018年10月3号,但是反过来就算不出来了,2018年10月3号是谁的生日是算不出来的,无法唯一确定。处理通过暴力破,听说把全世界计算机联合起来,跑50年才能算出来。
代码实现:
/**
* 定义Map通用用接口
*/
public interface Map<K, V> {
V put(K k, V v);
V get(K k);
int size();
interface Entry<K, V> {
K getKey();
V getValue();
V setValue(V v);
}
}
/**
* 底层实现
*/
public class HashMap<K, V> implements Map<K, V> {
//数据的存储结构(数组+链表)
Node<K, V>[] array = null;
//初始数组大小
private static int defaultLength = 16;
//加载因子
private static double LoadFactor = 0.75;
private static int size;
@Override
public V put(K k, V v) {
//懒加载机制
if (array == null) {
array = new Node[defaultLength];
}
//hash算法确定插入数组的位置
int index = position(k, defaultLength);
//扩容
if (size > defaultLength * LoadFactor) {
resize();
}
//放入元素(next两种情况:1,下个元素为空 2.下个元素不为空)
Node<K, V> node = array[index];
if (node == null) {
array[index] = new Node<>(k, v, null);
size++;
} else {
if (k.equals(node.getKey()) || k == node.getKey()) {
node.setValue(v);//更新(相当于去重)
} else {
array[index] = new Node<>(k, v, node);//返回新链表
size++;
}
}
return null;
}
private void resize() {
Node<K, V>[] temp = new Node[defaultLength << 1];
//重新计算散列值
for (int i = 0; i < array.length; i++) {
Node<K, V> node = array[i];
while (node != null) {
int index = position(node.getKey(), defaultLength);
Node<K, V> next = node.next;
node.next = temp[index];
temp[index] = node;
node = next;
}
}
//替换原来的array
array = temp;
defaultLength=temp.length;
temp=null;
}
private int position(K k, int length) {
int code = k.hashCode();
//取模算法
return code % (defaultLength - 1);
//求与算法
// return code&(defaultLength-1);
}
@Override
public V get(K k) {
if (array != null) {
int index = position(k, defaultLength);
Node<K, V> node = array[index];
while (node != null) {
if (node.getKey() == k) {
return node.getValue();
} else
node = node.next;
}
}
return null;
}
@Override
public int size() {
return size;
}
static class Node<K, V> implements Entry<K, V> {
K key;
V value;
Node<K, V> next;
public Node(K key, V value, Node<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
@Override
public K getKey() {
return this.key;
}
@Override
public V getValue() {
return this.value;
}
@Override
public V setValue(V v) {
V oldValue = this.value;
this.value = v;
return oldValue;
}
@Override
public String toString() {
return "Node{" +
"key=" + key +
", value=" + value +
", next=" + next +
'}';
}
}
public void print() {
Node<K, V> node;
if (array != null) {
for (int i = 0; i < array.length; i++) {
node = array[i];
System.out.print("[下标:" + i + "]");
while (node != null) {
System.out.print(node);
if (node.next != null) {
node = node.next;
} else {
node = null;
}
}
}
}
}
}
/**
* 测试hashMap接口
*/
@Slf4j
public class HashMapTest {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("1", "Linux");
map.put("2", "Windows");
map.put("3", "Unix");
map.print();
log.info("size="+map.size());
log.info("value="+map.get("009"));
}
}