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} }; 实现遍 ...
随机推荐
- js中遍历删除数组中的项(项目中遇到的问题解决)
代码如下: for (var key=0;key<$scope.pageContent.messages.length;key++){ if($scope.pageContent.message ...
- druid数据库密码加密程序编写
第一步:引入 druid-1.0.1.jar 架包 第二步: 编写程序 package nihao; import com.alibaba.druid.filter.config.ConfigTool ...
- HTML颜色、超链接设置
<html> <head> <title>显示的页面选项卡标题</title> <style type="text/css"& ...
- windows bat 设置代理上网脚本bat
取消IE代理服务器 ****************************************************************************************** ...
- CentOS7下搭建邮件服务器(dovecot + postfix + SSL)
CentOS 花了基本上两天的时间去配置CentOS7下的邮件服务器.其中艰辛太多了,一定得总结下. 本文的目的在于通过一系列配置,在CentOS 7下搭建dovecot + postfix + ...
- 使用excel结合线性规划求解Holt-Winters参数
其实上面这个是Holt-Winters无季节趋势模型, 上面的S(t)对应下面的a(t)——截距(平滑值) b(t)仍然对应b(t)——趋势,T对应k. ...
- Android之自定义属性
有些时候会觉得Android中提供的控件不能满足项目的要求,所以就会常常去自定义控件.自定义控件就不免会自定义属性.自定义属性大致需要三个步骤:在XML文件中定义自定义属性的名称和数据类型.在布局中调 ...
- Solr的主界面加登录权限
如题:效果如下图zu 只需两步: 1.tomcat-users.xml 下添加 <user username="admin" password="new-pas ...
- MySQL 拷贝数据库表方式备份,还原后提示 table xxx '' doesn`t exist
MySQL很强大,支持直接拷贝数据库文件快速备份,那数据库文件在哪里呢? 打开MySQL的配置文件 my.ini,找到 datadir 节点,如 datadir="D:/Program Fi ...
- composer的安装以及laravel框架的安装(一)
laravel号称世界上最好的php框架,没有之一,下面介绍它的安装 laravel学习交流qq群:293798134 composer的安装 : php开发者很多,并且在web开发领域占据绝对统治地 ...