Java19hashcode/hashset原理,Map,内部接口
Posted 码农编程录
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java19hashcode/hashset原理,Map,内部接口相关的知识,希望对你有一定的参考价值。
文章目录
1.Object类的hashcode方法:.toString(),.hashCode()
package com.itheima03.hash;
/*
* HashCode : 哈希码
* 1. Object类有一个方法:
* int hashCode() : 返回该对象的哈希码值。
* 1. 原理: 将对象的真正的内存地址(明文) 进行 哈希算法 加密之后产生的 哈希码值(密文)
* 2. 加密 :
* 明文 : 大家都看的懂东西 I love you
* 密文 : 明文经过加密算法变成密文 J mpwf zpv
* 加密算法: 数学 (凯撒加密: 字母按字母表右移动一位)
* 破解: 频率分析法 (e i -> d h),截获大量数据进行大数据分析e,i出现频率最高,密文中出现最多的是d,h
*
* 哈希算法: 公开
* 基本保证 一个明文 -> 一个密文 不同明文不同的密文
* 告诉你算法,告诉你密文, 算不出明文
*
* 3. 源码: public native int hashCode(); 本地方法
* native(本地关键字) 修饰的方法没有java方法体 (方法实现在JVM底层, 用C语言写的)
* 返回值 int (43亿不到的可能)
*
* 对象的真正的内存地址(无限种)->哈希码 (43亿不到的可能)。极端 : 多个明文 -> 同一密文 (哈希碰撞)
*/
public class HashcodeDemo {
public static void main(String[] args) {
Person p = new Person();
System.out.println(p); //com.itheima03.hash.Person@14ae5a5
// return getClass().getName() + "@" + Integer.toHexString(hashCode());
System.out.println(p.toString());//com.itheima03.hash.Person@14ae5a5
// 上面两个打印结果都一样
// 如下内存地址: 16进制哈希码值14ae5a5 和下面10进制相等
System.out.println(p.hashCode()); // 10进制: 21685669
}
}
class Person{
}
2.String类的hashcode方法:a97,碰撞
String s1 = “abc”,如下h就是hash值
(只看最后一个h),b是98,c是99。
package com.itheima03.hash;
// String类重写了Object的hashcode方法 (31算法)
public class StringHashCodeDemo {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "acD";
String s3 = "重地";
String s4 = "通话";
System.out.println(s1.hashCode());//96354
System.out.println(s2.hashCode());//96354 //和上面哈希碰撞
System.out.println(s3.hashCode()); //1179395
System.out.println(s4.hashCode()); //1179395
}
}
3.哈希表(HashSet)原理:元素不重复
hashcode(密文)为HashSet(HashSet底层数据结构是hash表)做铺垫。如下三个abc字符串都为96354。问题:HashSet如何判定这个元素是否跟已存在的元素是重复即如下[重地,通话,abc,acD]?Set不存重复元素。S3因为是new出来的,和S1,S2明文即真正的内存地址不一样。
下面S1,S4,S5,S6都是不重复元素。竖:暗文或暗文%16同,equal不一样。横:明文和暗文和equal都不一样。HashSet是效率最高的set且元素不重复,同一链表(竖)hashcode一样,但是链表如果太长查询慢,所以假如同一hash值(hashcode)碰撞了8次,链表重构为红黑树
。
%16因为数组长度为16
,同一链表上都hash碰撞,数组的第一个位置余数=0,第二个位置余数=1。。。16的容量为什么到16*0.75=12
就扩容了?再哈希rehash(余数重新算)这段时间内,16没满,我还有的用,如果rehash非常快就不用提前。
package com.itheima02.set;
import java.util.HashSet;
import java.util.Objects;
/*
* HashSet: 判定重复元素:(明文地址【内】,hash值【外】,equals【名字】)。
* 内同-》重复不插, 内不同 外不同 默认e不同-》横插, 内不同 外同 e不同-》竖插
*
* person类父类的Object: 1. hashcode
* 内存地址加密得到密文hash值(不同明文产生不同密文,刘亦菲两个内存地址不一样,密文hash值基本不会相同,万一碰撞了,还有下面2进行保障)
* 2. equals == 比较真正内存地址
*
* 需求: 两个对象就算地址不同, 但是所有属性一一相同, 就认为是同一元素
* 解决: 重写hashcode和equals方法 -> 类中的所有属性,重写规范见文章:【Java11】
*/
public class HashSetDemo02 {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<>();
set.add(new Person("高圆圆",18));
set.add(new Person("刘亦菲",19));
set.add(new Person("刘亦菲",19));//Person类继承Object类,new新地址
System.out.println(set);
}
}
class Person{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) { //alt+insert选equals()and hashCode()
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; //每个属性一一比对
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); //工具类Objects.java中hash方法中hashCode方法就是31算法,也是逐一遍历
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\\'' +
", age=" + age +
'}';
}
}
没有重写Person类的hashcode和equals方法
如下重写…如下就是hash表的应用:元素不重复,效率高
4.linkedHashset和Hashset区别:new A(){},coll.iterator().hasNext()
package com.itheima00.question;
import java.util.HashSet;
import java.util.LinkedHashSet;
/*
* Set: 不保证 存入和取出顺序一致
* HashSet : 无序
* LinkedHashSet : 多个另一个链表, 来记录存入的顺序,有序即取出有序,所以效率变低(少用)
*/
public class Demo01 {
public static void main(String[] args) {
HashSet<String> set = new LinkedHashSet<>(); //向上转型
set.add("张三");
set.add("李四");
set.add("王五");
set.add("马六");
System.out.println(set); //打印出有序的,LinkedHashSet不同于HashSet
}
}
package com.itheima00.question;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo02 {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<String>(); //Collection是接口
coll.add("张三");
coll.add("张三2");
coll.add("张三3");
// Iterator接口类型 变量 = 其实现类对象 (多态的向上转型)
/* Iterator<String> it = coll.iterator(); //Collection即coll是接口,接口调用方法执行子类ArrayList重写的iterator()
while(it.hasNext()){
String name = it.next();
System.out.println(name); //张三 张三2 张三3
}*/
// while(coll.iterator().hasNext()){ //不能这样把it换了链式编程,原因如下图
// String name = coll.iterator().next();
// System.out.println(name);
// }
}
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
public static void method01(){
MyClass mc = new MyClass();
A a = mc.test(); //右边返回必然是A接口实现类对象即向上转型,不需要new
//上行等同于Iterator<String> it = coll.iterator(); 不一定需要看到new
A a2 = new A() { //java中对象不一定看到new才放心 //new一个实现接口的匿名内部类A,使用{}具体实现接口
@Override
public void show() {
}
} ;
}
}
interface A{
void show();
}
class MyClass{
public A test(){ //返回A接口,不写void
// A a = new A(){}; //匿名内部类
// return a;
return new A() { //下面等同于上面两行,return A接口的子类对象
@Override
public void show() {
}
};
}
}
5.Map:Map.Entry < Integer,String > 是数据类型
Map和Collection是并列关系。
package com.itheima01.map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/*
Map中的方法
1. Map<K,V> <泛型>: K 表示作为键的类型,V表示值的类型
2. put: 存储键值对
1. 键值对存储到集合中 V put (K,V)
2. 如果存储了相同的键,覆盖原有的值
3. 返回值:一般返回null,如果存储了重复的键,返回被覆盖之前的值
3. get:通过键,取出键对应的值
1. V get(K),传递键,返回对应的值
2. 如果集合中没有这个键,返回null
4. remove:移除键值对
1. V remove(K),传递键,移除这个键值对
2. 返回值:移除之前的值(无此键,则返回null)
5. keySet: 将集合中所有的键,存储到Set集合中
6. entrySet:获取到Map集合中所有的键值对存入Set接中
7. size:获取map集合的大小
* Map:
* 1. key不可以重复
* 2. value可以重复
* 如果key存在,那么新value覆盖旧value
*/
public class MapDemo {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
map.put(4,"王五");
map.put(3,"马六"); //覆盖王五
System.out.println(map);//{1=张三,2=李四,3=马六,4=王五}
String name = map.get(5); //null,不是越界异常,无索引
String name = map.get(3); //从key获取value
System.out.println(name);//马六
//根据key删除key-value
map.remove(3);
System.out.println(map);
System.out.println(map.size()); //3,几个k
}
}
如下key不可重复,所以放到set集合(单例)。如下两种遍历方式都涉及set:
package com.itheima01.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapLoopDemo01 { //loop循环
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
Set<Integer> keySet = map.keySet(); //1.把key这一列取出来放到set集合中
for (Integer key : keySet) { //2.遍历这个set集合,取出每个key。keySet.for回车
String value = map.get(key); //3. 根据key获取value
System.out.println(key + "->" + value);
}
}
}
package com.itheima01.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapLoopDemo02 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"张三");
map.put(2,"李四");
map.put(3,"王五");
Set<Map.Entry<Integer,String>> entrySet = map.entrySet(); // 1. 把map转化成 Set<Entry> set
for(Map.Entry<Integer,String> entry : entrySet){ // 2. 遍历这样的set, 取出每个entry
//Entry是Map的内部接口,Map有很多Entry,Entry相当于Map属性一样。Map.是接口名直接调用
//如果import java.util.Map.Entry,则Map.Entry可换成Entry
Integer key = entry.getKey(); //3. 从这个键值对中,取键,再取值
String value = entry.getValue();
System.out.println(key + "--" + value);
}
}
}
package com.itheima03.impl;
import java.util.*;
/*
* HashMap是最常用的map实现类,因为快
* 1. key不可以重复,但是value可以重复
* 2. key如何判定重复? 先判断hashcode ,再判断equals
* Object: hashcode 和 equals 跟对象真正地址有关
* 重写了hashcode 和 equals,张三山东 覆盖 张三山西 新覆盖旧
*/
public class HashMapDemo {
public static void main(String[] args) {
// method01();
// method02(); //较method01交换了k和v
new LinkedHashSet<>(); //点进源码,底层是LinkedHashMap
new TreeSet<>(); //TreeMap
new HashSet<>(); //HashMap
//如下有序存取
LinkedHashMap<Person,String> map = new LinkedHashMap<>();
map.put(new Person("张三",18),"山西");
map.put(new Person("吴彦祖",20),"福州");
map.put(new Person("李四",19),"广东");
// map.put(new Person("张三",18),"山东");
Set<Person> keySet = map.keySet();
for (Person key : keySet) {
String value = map.get(key);
System.out.println(key + "---" + value);
}
}
private static void method02() {
HashMap<Person,String> map = new HashMap<>();
map.put(new Person("张三",18),"山西"); //key=Person 自定义类型
map.put(new Person("吴彦祖",20),"福州");
map.put(new Person("李四",19),"广东");
map.put(new Person("张三",18),"山东"); //new出来地址不同
Set<Person> keySet = map.keySet();
for (Person key : keySet) {
String value = map.get(key);
System.out.println(key + "---" + value);
}
// HashSet<Object> set = new HashSet<>(); //点进HashSet看源码
//HashSet【collection接口】的底层是HashMap【Map接口】 ,只不过hashset只使用了HashMap key这一列,value这一列不用
}
private static void method01() {
HashMap<String, Person> map = new HashMap<>();
map.put("1号",new Person("张三",18)); //value=Person 自定义类型
map.put("2号",new Person("李四",19));
map.put("3号",new Person("李四",19));
map.put("1号",new Person("王五",20))以上是关于Java19hashcode/hashset原理,Map,内部接口的主要内容,如果未能解决你的问题,请参考以下文章