2021-04-12

HashMap集合

1. HashMap介绍

1.1 HashMap的简介

  1. HashMap是Map集合接口的子类之一,是常用的Map之一。
  2. HashMap是以 key-value 的形式存储数据的。允许key为null,但只允许一个key为null;允许value为null。
  3. HahMap中key不能重复,value可以重复。
  4. HashMap的初始容量为16,默认加载因子为0.75。
  5. HashMap是线程不安全的。

1.2 HashMap的数据结构(1.7和1.8)

JDK1.7时的数据结构是 数据 + 链表
2021-04-12当要put数据是,先会计算key的hash值,存放到相应的数组索引位置,如果当前位置没有值,则直接存储;如果当前位置上有值,则调用重写的equals方法比较key,如果相同则覆盖value,如果不同则说明是不同的值则插入到链表的头部

JDK1.8时的数据结构是 数据 + 链表 + 红黑树
在1.8中,hashMap在new的时候不会初始化容量,只有当put的时候才会进行初始化
2021-04-12HashMap 在 Java 8 中的实现增加了红黑树,当链表节点达到 8 个的时候,会把链表转换成红黑树,低于 6 个的时候,会退回链表。究其原因是因为当节点过多时,使用红黑树可以更高效的查找到节点。毕竟红黑树是一种二叉查找树。

1.4 HashMap需要同时重写hashCode方法和equals方法的原因?

hashCode和equals方法是Object中的原生方法:
equals:比较的是对象的地址值是否相等
hashcode():返回的是对象的地址,所以这种情况下不同对象的hashcode肯定不同

为什么要重写hashCode方法
hashMap.put(“a”,“123”) ,hashMap.put(“a”,“123”)时;如果不重写hashcode,那么同样的一个键值对 , 唯一性得不到保证,所以一定要重写hashCode方法。因为Object的hashcode返回的是内存地址,可能造成的存储结果如下:

0 1 2 3 4 5
(“a”,“123”) (“a”,“123”)

为什么要重写equals方法:
hashMap.put(“a”,“123”) ,hashMap.put(“b”,“123”)时;两次put元素的hash值相同,则会用链表的方式存储,存储形式如下:

0 1 2 3 4 5
(“a”,“123”)
(“a”,“123”)

当要get(“a”)时,会进行hashCode判断,知道去索引为0的位置查找,就会发现有2个元素,此时就无法判断要取出的数据为哪个。所以必须要重写equals方法才能判断对象的值

总结:
hashCode相同,值不一定相同;hashCode不同,值一定不同
hashCode保证键的唯一性,equals保证值的唯一性,所以需要同时重写

2. HashMap方法原理

2.1 put方法

2021-04-12put的过程:

1:对key的hashCode() 做hash计算,然后根据hash值再计算Node的存储索引位置。
2:检查当前数组是否为空,为空就进行初始化,初始化容量为16,默认加载因子为0.75
3:如果没有哈希碰撞,直接放入桶中;如果有哈希碰撞,比较equals方法,判断是否相同,如果相    同则替换value;不同则以链表的形式存入尾部
4:如果哈希碰撞导致链表长度大于8,则将链表转为红黑树
5:当桶数量超过 容量*负载因子 时,就进行扩容,扩容为原数组长度的两倍

2.2 get方法

get方法的过程:

对 key 的 hashCode() 做 hash 计算,然后根据 hash 值再计算桶的 index
如果桶中的第一个节点命中,直接返回;
如果有冲突,则通过 key.equals(k) 去查找对应的 entry
    若为树,则在红黑树中通过 key.equals(k) 查找,O(logn);
    若为链表,则在链表中通过 key.equals(k) 查找,O(n)。

2.3 HashMap的扩容机制

数组容量是有限的,数据多次插⼊的,到达⼀定的数量就会进⾏扩容,也就是resize。
影响resize有两个因素:
Capacity:HashMap当前⻓度。
LoadFactor:负载因⼦,默认值0.75f。

    /**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

就⽐如当前的容量⼤⼩为100,当你存进第76个的时候,判断发现需要进⾏resize了,那就进⾏扩容。
扩容的步骤
分为两步
扩容:创建⼀个新的Entry空数组,⻓度是原数组的2倍。
ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。
需要重新Hash的原因:
是因为⻓度扩⼤以后,Hash的规则也随之改变。
Hash的公式—> index = HashCode(Key) & (Length - 1)

3. HashMap用法简单使用

public static void main(String[] args) {
		HashMap<String,String> hm=new HashMap<String,String>();
		
		hm.put("it001", "lh");
		hm.put("it003", "dc");
		hm.put("it004", "ch");
		hm.put("it002", "dc");
		hm.put("it001", "lhh");//键的唯一性
		hm.put("it005","lh");
		hm.put("dc","lh");
		hm.put("中","lh");
		
		System.out.println(hm);
		//输出结果
		//{it004=ch, it003=dc, it005=lh, it002=dc, it001=lhh, 中=lh, dc=lh}
		
		System.out.println(hm.get("it001"));//lhh 
		System.out.println(hm.get("it002"));//dc 
		
		//遍历输出
		Set<String> set=hm.keySet();
		for(String key:set) {
		   String value=hm.get(key);//键找值
		   System.out.println(key+"---"+value);
		}
	}

允许key为null,但只允许一个key为null;允许value为null。

public static void main(String[] args) {
		HashMap<String,String> hm=new HashMap<String,String>();
		hm.put(null, "word");
		hm.put("word", "word");
		hm.put("wor", null);
		hm.put(null, null);
		
		System.out.println(hm);//{null=null, wor=null, word=word}
		
	}

以上就是对HashMap的一个理解,如果有问题感谢提出,一起进步。

上一篇:centos7环境下php网站通过webshell安全扫描工具对系统做检测


下一篇:java--遍历字符个数