ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

HashMap集合

2022-03-05 12:01:13  阅读:184  来源: 互联网

标签:HashMap s2 s1 equals hashCode 链表 集合


Map接口实现类之一:HashMap集合

1. 底层是哈希表/散列表的数据结构

哈希表:是一个一维数组,这个数组中的每一个元素是一个单向链表(是数组和单向链表的结合体)

/*源代码:
 * public class HashMap<K,V>{
 *         //1. HashMap的底层是一个一维数组
 *         transient Node<K,V>[] table;
 *       //2. 有静态内部类:HashMap.Node
 *      static class Node<K,V> implements Map.Entry<K,V> {
           final int hash; //哈希值(哈希值是key的hashCode()方法执行的结果,哈希值hash通过哈希函数/算法,可以得到数组的下标)
           final K key;   //存储到Map集合中的key
           V value;       //存储到Map集合中的value
           Node<K,V> next; //同一个单链表上的下一个结点的内存地址
           } 
 * }*/

2.掌握 map. put(k,v) 和 v = map.get(k)方法的实现原理

1. map. put(k,v) 存

  1. 先将k和v封装到Node对象中
  2. 底层会调用k的hashCode()方法得到hash值,通过哈希函数/算法,可以得到数组的下标
  3. 下标位置上如果没有任何的元素,就把Node添加到这个位置上了;如果下标对应的位置上有单链表,就会拿着k和链表上的每一个结点中的k进行equals,若所有的equals方法返回的都是false,那么这个新节点就将被添加到链表的末尾;若其中有一个equals返回了ture,那么原来结点的value 就会被新节点大的value所覆盖。

2. v = map.get(k) 取

  1. 底层会调用k的hashCode()方法得到hash值,通过哈希函数/算法,可以得到数组的下标
  2. 通过数组的下标快速定位某位置,若下标位置上如果没有任何的元素,就返回null;如果下标对应的位置上有单链表,就会拿着k和链表上的每一个结点中的k进行equals,若所有的equals方法返回的都是false,那么get方法就返回null;若其中有一个equals返回了ture,那么此结点所对应的value就是要找的,get方法就返回此结点所对应的value。

3. 解释:HashMap集合的key的特点:无序和不可重复 why?

(1)无序: 因为往集合中存时,不一定挂到哪个单链表上;所以存进去的顺序和取出时的不同
(2)不可重复:在 map. put(k,v) 方法中,equals方法来保证HashMap集合的key不可重复,若key重复了,value 将会被覆盖

4. 解释:哈希表的随机增删和查询效率都很高?

因为增删是在链表上完成,查询也不需要将所有数据都扫描一遍
数组在查询方面效率较高,随机增删效率低;而单向链表在查询方面效率较低,随机增删效率高;哈希表将二者结合在一起,发挥其优点。

3. HashMap集合的key部分和放在HashSet集合中的元素,要同时重写hashCode方法和equals方法

以上可知 :HashMap集合的key,会先后调用hashCode()方法和equals方法,要重写这两个方法;又因为HashMap集合的key是放到HashSet集合,所以HashSet集合也要重写这两个方法。

  1. 注意:同一个单向链表上所有结点的hash值相同,因为它们所对应的数组下标相同,但同一个单向链表上所有结点的k和k的equals方法返回的都是false,都不相等
  2. hashCode()方法重写时:若返回值是个固定的值,那么会导致底层的哈希表变成了纯单向链表;若返回值都设定为不同的值,那么会导致底层的哈希表变成了一维数组;称以上两种情况为 :散列分布不均匀;
  3. 散列分布均匀:100个元素,对应了10个数组元素,即对应了10个单向链表,每个单向链表上有10个结点
  4. hashCode()方法和equals方法都是子类继承于祖先类Object中的,其中,equals方法用==,比较的是两个对象的内存地址,要重写,来比较二者内容
  5. HashMap集合的默认初始化容量是16,默认加载因子是0.75,(当HashMap集合底层的数组的容量达到75%的时候,数组开始扩容)
  6. 官方推荐: HashMap集合的初始化容量必须是2的倍数;因为要达到散列均匀,有利于提高HashMap集合的存取效率。
package 集合练习;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
 
public class Map3 {

	public static void main(String[] args) {
		
		Map<Integer, String> map = new HashMap<>();
		//key的类型是Integer,Integer的hashCode()和equals方法都重写了
		
		map.put(1, "diyi");//1是自动装箱
		map.put(2, "dier");
		map.put(3, "disan");
		map.put(3, "disi"); 
		
		System.out.println(map.size());//3
		//遍历
		Set<Map.Entry<Integer, String>> set = map.entrySet();
		for(Map.Entry<Integer, String> entry:set) {
			System.out.println(entry.getKey()+"="+entry.getValue());
		}
		
	}//验证了:key重复的时候,value会被新的所覆盖; key的特点:无序不可重复
/*
 * 3
1=diyi
2=dier
3=disi
*/
}

如果一个类的equals方法重写了,那么hashCode方法必须重写!equals方法返回若是true, hashCode方法的值必须一样

  1. 向Map集合中存以及取,都是先调用 key的hashCode方法,再调用equals方法。
  2. 如果equals方法返回true,表示两个对象相同,即在同一个单向链表上的同一个结点来说,它们的哈希值hash值也相同,那么hashCode方法的返回值相同
package 集合练习;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class Map4 {

	public static void main(String[] args) {
	 
		Student s1 = new Student("li");
		Student s2 = new Student("li");
//		//重写equals方法之前
//		System.out.println(s1.equals(s2)); 
//		System.out.println("s1的hashCOde="+s1.hashCode());
//		System.out.println("s2的hashCOde="+s2.hashCode());
/*false
s1的hashCOde=292938459
s2的hashCOde=917142466*/
		//重写equals方法之后
//		System.out.println(s1.equals(s2)); 
//		System.out.println("s1的hashCOde="+s1.hashCode());
//		System.out.println("s2的hashCOde="+s2.hashCode());
/*true
s1的hashCOde=292938459
s2的hashCOde=917142466*/
		
//		Set<Student> students = new HashSet<>();
//		students.add(s1);
//		students.add(s2);
//		System.out.println(students.size());//2
//		s1.equals(s2)是true,表明s1和s2是一样的,那么往HashSet集合(不可重复)中放,应该只能放一个,相矛盾;
		
		
		//借助工具重写equals和hashcode
		System.out.println(s1.equals(s2)); 
		System.out.println("s1的hashCOde="+s1.hashCode());
		System.out.println("s2的hashCOde="+s2.hashCode());
/*
 * true
s1的hashCOde=3484
s2的hashCOde=3484*/
		Set<Student> students = new HashSet<>();
		students.add(s1);
		students.add(s2);
		System.out.println(students.size());//1
	}
	/*总结:
	 *如果一个类的equals方法重写了,那么hashCode方法必须重写
	 *equals方法返回若是true, hashCode方法的值必须一样*/
	

}
class Student{
	
	private String name;

	public Student() {
		super();
	}

	public Student(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
//	//手动重写equals方法:
//	public boolean equals(Object obj) {
//		if(obj==null || !(obj instanceof Student)) return false;
//		if(obj==this) return true;
//		Student student  = (Student)obj;
//		return this.name.equals(student.name);
//	}
	
	//借助工具重写equals和hashcode

	@Override
	public int hashCode() {
		return Objects.hash(name);
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		return Objects.equals(name, other.name);
	}	
}

补充:
在JDK8之后,若哈希表的单向链表中的元素超过8个,单向链表这种数据结构就会变成红黑树这种数据结构。
当红黑树上的结点的数量小于6时,会重新把红黑树变成单向链表这种数据结构
这种方式是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率。

标签:HashMap,s2,s1,equals,hashCode,链表,集合
来源: https://www.cnblogs.com/yang123-456/p/15967521.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有