Java基础知识点2:hashCode()方法
hashCode()方法基本实现
hashCode方法是Java的Object类所定义的几个基本方法之一。我们可以深入到Object类的源码中去查看:
public native int hashCode();
其中native关键字表明这个函数是由非java语言来实现的,这个函数的功能就是返回这个对象在内存中的地址。
hashCode()方法的应用
大部分类都会重新覆写一下hashCode方法,原因有很多。那么这个方法主要被应用在什么场景下呢?一个非常重要的应用就是当我们处理散列集合类的时候,比如HashSet,HashMap,HashTable。
java之所以要设计这三种散列集合类,目的就是能够在常数时间复杂度O(1)下,完成“确定一个元素是否在一个集合中”,“插入元素”,“删除元素”等操作。采取的思路就是使用哈希表。这是一个我们经常见到的数据结构。我们在实现一个哈希表时最重要的一步操作就是通过这个元素的关键字来计算它的哈希值,从而就可以通过这个哈希值来查询哈希表,看看这个元素是否在集合中。那么hashCode方法的作用就是提供这个元素的关键字。
我们可以具体看一下HashMap里面几个重要方法的实现:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}
这个方法是向集合中插入一个新的“键值对”元素,其中:
第4行首先通过hashCode计算这个元素的关键字。
第4行调用hash()函数,计算这个关键字的哈希值。
第5行计算这个哈希值对应于哈希表中的哪个位置,HashMap会维护一个哈希表 Entry[] table。每一个位置对应一个哈希值,并且任意一个Entry[i]元素其实是一个链表头,这个链表中存放着在集合中所有哈希值为i的元素。
第6~13行就要遍历这个table[i]所管理的链表,并且查询这个链表中是否已经存在了关键字为k的元素。如果查找到了,则用新的value去覆盖旧的value,并且函数会把旧的value返回。
第16~18行,如果运行到这里代表这个一个全新的元素,此时就把它加到集合中。
另外其他的方法比如get,也会运用到hashCode方法。
hashCode()方法的覆写
hashCode()方法在Object的实现中,是返回对象的内存地址,但是通常在第一个类的时候,我们经常会覆写hashCode()方法,比如在Integer类中,hashCode()就是返回这个Integer的值。
所以这就会造成多个问题,或者说多个结论:
1. 如果两个引用hashCode()返回的值相同,并不能代表这两个引用指向的是同一个对象。
2. 如果两个引用指向的是同一个对象,则它们的hashCode()方法一定返回一样的值。
3. 如果两个引用的hashCode值返回的是不一样的值,则这两个引用一定指向的不是同一个对象。
实例1:
public static void main(String[] args) {
Integer i1 = new Integer(2);
Integer i2 = new Integer(2);
System.out.println("i1 hashCode is "+i1.hashCode()+" i2 hashCode is "+i2.hashCode());
if(i1.hashCode() == i2.hashCode())
System.out.println("i1, i2 have same hashCode");
else
System.out.println("i1, i2 have different hashCode");
if(i1 == i2)
System.out.println("i1, i2 are same");
else
System.out.println("i1, i2 are different");
}
这个例子就是说明结论1的,i1,i2都是Integer对象,且值都是2,Integer的hashCode方法返回的就是Integer的值,所以它们的hashCode相同,但是i1和i2指向的是不同对象,所以最后输出的结果是:
i1 hashCode is 2 i2 hashCode is 2
i1, i2 have same hashCode
i1, i2 are different
hashCode()方法和equals()方法配合使用
在自定义新的类时,经常会覆写equals方法,但是这里要注意,java有个规定:如果你覆写了某个类的equals方法,那么你一定也要覆写它的hashCode()方法。覆写的原则就是保证如果两个引用调用equals方法时如果返回true,则这两个引用的hashCode()的返回值也需要是一样的。
为什么要这样做,我们可以看一下下面的这种情况:
public class test {
public static void main(String[] args) {
People p1 = new People("zzq", 24);
Map<People, Integer> map = new HashMap<People, Integer>();
map.put(p1, 1);
System.out.println(map.get(new People("zzq", 24)));
}
} class People {
String name;
int age; public People(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
这个类中定义了一个其他的类,People。并且新建了一个HashMap<People, Integer>。并且将一个新建的people对象加入其中,然后我们想按照那个People对象的值新建另一个同样值的对象new People("zzq", 24),并且通过新建的对象找到它的值value。但是结果返回为null。我们希望它能够返回1。
为什么这里不可以?主要就是因为没有实现hashCode()方法,此时这个类的hashCode方法仍旧是返回内存地址,所以二者的hashCode不同。
此时我们可以看下 get() 方法
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
其中首先就是第3行,首先计算出你要找的元素的关键字,以及这个关键字的hashCode,由于此时hashCode是内存地址,所以一开始存入的p1,和后来在get函数输入参数列表中新建的new People()对象,是具有不同的hashCode的。自然得出的hash值也是不一致的,所以一定找不到存放p1的那个哈希表项。所以一定会返回null。
如果我覆写一下hashCode()方法和equals()方法:
@Override
public int hashCode() {
return name.hashCode()+this.age;
} @Override
public boolean equals(Object obj) {
return this.name.equals(((People)obj).name) && this.age == ((People)obj).age;
}
此时程序就会返回1了。
Java基础知识点2:hashCode()方法的更多相关文章
- Java基础知识点(三)
前言:准备将Java基础知识点总结成一个系列,用于平常复习并加深理解.每篇尽量做到短小精悍,便于阅读. 1.Math类中相关函数 Math.floor(x):返回不大于x的最大整数.eg:Math.f ...
- Java基础知识点(一)
前言:本篇随笔,主要记录Java的基础知识点,不管是用于项目或者面试中,笔者认为都非常有用,所以将持续更新...... 1.Java的访问权限 Java中有四种访问权限:默认访问权限.public.p ...
- JAVA基础知识点总结(全集)
1.JAVA简介 1.1java体系结构:j2se,javaweb,j2ee 1.2java特点:平台无关(虚拟机),垃圾回收(使得java更加稳定) 1.3 JDK与JRE,JDK:java开发环境 ...
- JAVA基础(1)之hashCode()
JAVA基础(1)之hashCode() 看到一篇关于hashCode的文章(),写的很详细明白,瞬间有种恍然大悟的感觉,所以特地转过来.原文:http://blog.csdn.net/fenglib ...
- Java基础知识点(四)
前言:记录Java基础知识点,方便熟悉与掌握. 1.面向对象的"六原则一法则" “六原则一法则”:单一职责原则.开闭原则.依赖倒转原则.里氏替换原则.接口隔离原则.合成聚合复用原则 ...
- Java基础知识点(二)
前言:Java的基础知识点不能间断. 1.Array和ArrayList的区别 关于Array的用法,参看:http://blog.csdn.net/b_11111/article/details/5 ...
- Java基础知识点总结
前言 本文主要是我之前复习Java基础原理过程中写的Java基础知识点总结.Java的知识点其实非常多,并且有些知识点比较难以理解,有时候我们自以为理解了某些内容,其实可能只是停留在表面上,没有理解其 ...
- Java基础 之 System.getProperty()方法
Java基础 之 System.getProperty()方法大全 public static void main(String[] args) { System.out.println(" ...
- java中equals和hashCode方法随笔二
前几天看了篇关于java中equals和hashCode方法的解析 1.Object类中的equals方法和hashCode方法. Object类中的equals和hashCode方法简单明了,所有的 ...
- java基础知识点补充---二维数组
#java基础知识点补充---二维数组 首先定义一个二维数组 int[][] ns={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16} }; 实现遍 ...
随机推荐
- Javascript和HTML:
JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标 ...
- SQL 事务回滚
事务(Transaction)是并发控制的单位,是用户定义的一个操作序列.这些操作要么都做,要么都不做,是一个不可分割的工作单位.通过事务,SQL Server能将逻辑相关的一组操作绑定在一起,以便服 ...
- LogStash配置、使用(三)
LogStash配置 官方文档:https://www.elastic.co/guide/en/logstash/current/index.html 查看yum安装路径 rpm -ql logsta ...
- Android热修复AndFix
热修复主要用来修复代码.修复bug.添加独立的功能,他的原理主要是操作PathClassLoader.DexClassLoader. PathClassLoader是类加载器,DexClassLoad ...
- 开发unity DLL插件
最近开发一款设备的SDK,想着要开发unity版本,怎么做呢?首先想到的就是在外部编写相关的驱动程序然后集成成几个dll作为unity的SDK使用了.So,我就开始了unity外部插件的研究之旅了. ...
- Fedora 23 忘记root密码
方法:进入单用户模式改密码 进入grub后,按e进入编辑模式.找到以“linux"开头的那一行,在末尾加” rw init=/bin/bash".ctrl-x启动 (grub2用c ...
- 数据存储_SQLite (2)
SQL代码应用示例 一.使用代码的方式批量添加(导入)数据到数据库中 在ios项目中使用代码批量添加多行数据示例 代码示例: 1 // 2 // main.m 3 // 01-为数据库添加多行数据 4 ...
- Spring系列之Spring常用注解总结
传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点:1.如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大:如果按需求分开.xml文件 ...
- 浅谈MVC中路由
引言 学习ASP.NET MVC 路由这一关是肯定必不可少的.这一节,我们就来简单介绍下MVC中的路由机制.简单的路由机制相信大家都已了解,这一节主要介绍路由中很少使用的部分. 使用静态URL片段 在 ...
- 在Heroku部署时,无法加载 css,js,图片资源解决办法
解决方案: 首先查看Gemfile, 确保group :production do 里添加了 gem "rails_12factor", '0.0.2' 然后在本地执行 rails ...