java-散列和散列码
来源:互联网 发布:sql replace替换多个 编辑:程序博客网 时间:2024/06/05 06:49
正在看《Thinking in java》学习散列码相关的知识,在这总结一下吧。
先来看一个例子
class Student{ protected int id;//当前类的成员与继承该类的类能访问. public Student(int id) { this.id = id; } @Override public String toString() { return "Student #" + id; }}class Score{ private static Random random = new Random(47); private int score = random.nextInt(100); @Override public String toString() { return String.valueOf(score); }}public class HashCodeTest { public static <T extends Student> void studScore(Class<T> type) throws Exception{ Constructor<T> stud = type.getConstructor(int.class); //利用反射机制来实例化及使用Student类和任何从Student派生出来的类 Map<Student, Score> map = new HashMap<>(); for (int i = 0; i < 10; i++) { map.put(stud.newInstance(i), new Score()); } System.out.println("map" + map); Student student = stud.newInstance(3);//使用id为3的Student作为键 System.out.println("寻找学生: " + student); if (map.containsKey(student)) { System.out.println(map.get(student)); }else { System.out.println("此学生不存在"); } } public static void main(String[] args) throws Exception{ studScore(Student.class); }}
输出
map{Student #1=55, Student #4=61, Student #5=29, Student #3=61, Student #8=22, Student #7=0, Student #0=58, Student #2=93, Student #9=7, Student #6=68}寻找学生: Student #3此学生不存在
但是结果它无法找到id为3的键。问题出在Student是自动继承自基类Object,这里是使用Object的hashCode()方法生成散列码,默认是使用对象的地址计算散列码。第一个Student(3)的实例和第二个的散列码是不同的。所以无法找到。
所以需要恰当的覆盖hashCode()方法。并且同时覆盖equals方法。因为HashMap使用equals()判断当前的键是否与表中存在的键相同
equals()满足下列条件:
1)自反性 对于任意x, x.equals(x)必定返回true。
2)对称性 对于任意x,y,x.equals(y)与y.equals(x)的返回值相同。
3)传递性 对于任意x,y,z,如果x.equals(y)与y.equals(z)返回值相同,那么x.equals(z)返回值也相同。
4)一致性 对于任意的x,y,无论x.equals(y)执行多少次,返回值要么是true,要么为false。
5)对于任意x != null, x.equals(null)返回false。
所以要使用自己的类作为HashMap的键,必须同时覆盖hashCode()和equals().
class Student2 extends Student{ public Student2(int id) { super(id); } @Override public int hashCode() { return id;//返回id作为散列码 } @Override public boolean equals(Object obj) { return obj instanceof Student2 && (id == ((Student2)obj).id); //instanceof检查了此对象是否为null,然后基于每个对象中实际的id进行比较 }}public class HashCode2 { public static void main(String[] args) throws Exception { HashCodeTest.studScore(Student2.class); }}
输出
map{Student #0=58, Student #1=55, Student #2=93, Student #3=61, Student #4=61, Student #5=29, Student #6=68, Student #7=0, Student #8=22, Student #9=7}寻找学生: Student #361
为速度而散列
线性查询是最慢的查询方法
散列将键保存在某处,以便能够很快找到。存储一组元素最快的数据结构是数组,所以使用它来表示键的信息。
数组并不保存键本身,而是通过键对象生成一个数字,将其作为数组的下标,这个数字就是散列码。
冲突有外部链接处理,数组并不直接保存值,而是保存值得list。然后对list中的值使用equals()方法进行线性的查询。(这部分的查询会比较慢)
散列表的“槽位”(slot)通常被称为桶位(bucket),Java的散列函数使用2的整数次方(求余和除法是最慢的操作,使用2的整数次方长度的散列码,可以用掩码代替除法,可以减少get()中%操作的开销)。
覆盖hashCode()
无论何时,对同一个对象调用hashCode()都应该生成同样的值。且不能依赖易变的数据。
也不应该使hashCode()依赖于唯一性的对象信息。
以String类为例,如果程序中有多个String对象,都包含相同的字符串序列,那么这些String对象都映射到同一块内存区域。
public class StringTest { public static void main(String[] args) { String str1 = new String("hello"); String str2 = new String("hello"); System.out.println(str1.hashCode()); System.out.println(str2.hashCode()); System.out.println(str1.equals(str2)); System.out.println(str1 == str2); //双等号就是比较的栈里面的内容,原始数据类型和地址都是放在栈里面的。而equals则是根据地址拿到堆里面的内容进行比较。 }}
输出
9916232299162322truefalse
对String而言,hashCode()明显是基于String的内容的。根据对象的内容生成散列码,散列码不一定是独一无二,但是通过hashCod()和equals()必须能够完全确定对象的身份
来看看String的源码
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; } //String类中的hashCode计算方法还是比较简单的,就是以31为权,每一位为字符的ASCII值进行运算.public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
- java-散列和散列码
- Java散列和散列码的实现
- Java中队列和堆栈
- Java基础-理解散列与散列码
- 索引和散列
- 索引和散列
- java 散列与散列码探讨 ,简单HashMap实现散列映射表执行各种操作示列
- Java序列化和反序列化
- java 序列化 和反序列化
- java 序列化和反序列化
- java 序列化和反序列化
- [java]序列化和反序列化
- Java文字列的append和concat
- java序列化和反序列化
- java 序列化和反序列化
- java序列化和反序列化
- JAVA序列化和反序列化
- Java 序列化和反序列化
- Android百度地图,HelloMap(一)
- 多线程的同步和互斥
- Lua - 4
- 设置浏览器允许跨域访问
- DAY20:leetcode #55 Jump Game
- java-散列和散列码
- 国内服务器:Linux 第一步 更换更新源
- NSArray
- git使用备忘录
- Android--CardView详解
- CUDA存储器模型
- 推荐的网站
- Struts2单文件下载
- MyBatisSQL配置文件中parameterType