HashMap(基于JDK1.8版本实现)
底层实现方式:JDK1.8的hashmap 数组+链表+红黑树
当冲突同一index个数大于8时,才会使用红黑树
HashMap(基于JDK1.7版本实现)
注意,存在死循环情况:
jdk1.7 多线程情况下扩容会产生闭环,取数据的时候就会进行死循环,因为get 是for遍历 用的是e.next 会导致死循环
底层实现方式:JDK1.7的hashmap 数组+链表(单链表)
1.创建Map接口
public interface ExtMap<K, V> {
// 向集合中插入数据
public V put(K k, V v);
// 根据k 从Map集合中查询元素
public V get(K k);
// 获取集合元素个数
public int size();
interface Entry<K, V> {
K getKey();
V getValue();
V setValue(V value);
}
}
2.创建HashMap实现
public class MyHashMap<K,V> implements ExtMap<K, V> {
// 1.定义table 存放HasMap 数组元素 默认是没有初始化容器 懒加载
Node<K, V>[] table = null;
// 2. 实际用到table 存储容量 大小
int size;
// 3.HashMap默认负载因子,负载因子越小,hash冲突机率越低, 根据每个链表的个数
float DEFAULT_LOAD_FACTOR = 0.75f;
// 4.table默认初始大小 1
static int DEFAULT_INITIAL_CAPACITY = 1<<4; // 1<<4二进制移位,效率高 1<<4其实就是16
@Override
public V put(K key, V value) {
//1.判断 table是否为空
if(table==null){
table=new Node[DEFAULT_INITIAL_CAPACITY];
}
/* 2.判断size是否大于DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR
* 如果大于等于,进行扩容
*/
if(size>=(DEFAULT_INITIAL_CAPACITY*DEFAULT_LOAD_FACTOR)){
//进行扩容
resize();
}
//3.计算哈希值hash 取模得到下标值
int index = getIndex(key, DEFAULT_INITIAL_CAPACITY);
/*4.若该下标位置 为空 ,则该节点存放到数组table的index下标位置
* 若不为空,查看该节点的key是否等于key
* 若不相等,继续查下下个节点
* 若相等,更换值,并返回旧的值
*/
Node<K, V> node = table[index];
if(node==null){
node=new Node<K,V>(key, value, null);
size++;
}else {
//取多一个值节点
Node<K, V> newNode = node;
while(newNode!=null){
//若存在该值,覆盖旧的值,并返回 旧的值
if(newNode.getKey().equals(key)||newNode.getKey()==key){
return newNode.setValue(value);
}else {
//若该到了链表最后的节点为空,则将新的节点添加到链表的头部
if(newNode.next==null){
node=new Node<K,V>(key, value, node);
size++;
}
}
newNode=newNode.next;
}
}
//将该节点存放到index下标位置
table[index]=node;
return null;
}
//hashmap数组扩容
private void resize() {
Node<K, V>[] newtable = new Node[DEFAULT_INITIAL_CAPACITY << 1];
for(int i = 0;i<table.length;i++){
Node<K, V> oldNode = table[i];
while(oldNode!=null){
//1.重新计算index 获取扩容后的下标值
int index = getIndex(oldNode.getKey(), newtable.length);
//2.保存 下个节点
Node oldnext=oldNode.next;
//3.newtable[index]==>链表 直接链接到oldNode的next
oldNode.next=newtable[index];
//4.将重新链接好的链表 覆盖到 数组index对应的位置
newtable[index]=oldNode;
//5.将之前2步骤保存的节点 作为下一个节点 继续执行
oldNode=oldnext;
}
//每完成数组一个下表位置的链表,则清空,减少内存占用以及数据的多余
table[i] = null;
}
//执行完后,将newtable覆盖到原table,减少内存的占用
table=newtable;
DEFAULT_INITIAL_CAPACITY = newtable.length;
newtable = null;// 将 对象变为不可达对象 垃圾回收
}
@Override
public V get(K k) {
Node<K, V> node = getNode(table[getIndex(k, DEFAULT_INITIAL_CAPACITY)],k);
return node == null ? null : node.getValue();
}
public Node<K, V> getNode(Node<K, V> node,K k){
while(node!=null){
if(node.getKey().equals(k)||node.getKey()==k){
return node;
}
node=node.next;
}
return null;
}
@Override
public int size() {
return size;
}
//取下标值
public int getIndex(K k,int length){
int index = k == null ? 0 : k.hashCode() % length;
return index;
}
//定义节点
class Node<K , V> implements Entry<K, V>{
private K key;
private V value;
private Node<K,V> next;
@Override
public K getKey() {
return this.key;
}
@Override
public V getValue() {
return this.value;
}
@Override
public V setValue(V value) {
//返回旧的值
V oldValue=this.value;
this.value=value;
return oldValue;
}
public Node(K key, V value, Node<K, V> next) {
super();
this.key = key;
this.value = value;
this.next = next;
}
}
// 测试方法.打印所有的链表元素
void print() {
for (int i = 0; i < table.length; i++) {
Node<K, V> node = table[i];
System.out.print("下标位置[" + i + "]");
while (node != null) {
System.out.print("[ key:" + node.getKey() + ",value:" + node.getValue() + "]");
node = node.next;
}
System.out.println();
}
}
}
测试代码
public class TestMyHashMap {
public static void main(String[] args) {
// 基于原则 :后进先出
MyHashMap myHashMap = new MyHashMap<String, String>();
myHashMap.put("1号", "1号");// 0
myHashMap.put("2号", "2号");// 1
myHashMap.put("3号", "3号");// 2
myHashMap.put("4号", "4号");// 3
myHashMap.put("6号", "6号");// 4
myHashMap.put("7号", "7号");//5
myHashMap.put("14号", "14号");//6
myHashMap.put("22号", "22号");//7
myHashMap.put("26号", "26号");//8
myHashMap.put("27号", "27号");//9
myHashMap.put("28号", "28号");//10
myHashMap.put("66号", "66");//11
//myHashMap.put("30号", "30号");
System.out.println("扩容前数据....");
myHashMap.print();
//当size大于大于等于 DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR 进行扩容
System.out.println("扩容后数据....");
myHashMap.put("31号", "31号");
myHashMap.put("66号", "123466666");
myHashMap.print();
// 修改3号之后
System.out.println(myHashMap.get("66号"));
// System.out.println("扩容之前获取数据:" + myHashMap.get("1号"));
// myHashMap.print();
// System.out.println();
// // myHashMap.put(14 + "号", 14 + "号");
// // myHashMap.put(1 + "号", "4444号");
// System.out.println("扩容之后获取数据:" + myHashMap.get("1号"));
// myHashMap.print();
}
}