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} }; 实现遍 ...
随机推荐
- 菜鸟的Python学习之路(流水账)
揭开Python的面纱 开始是因为别人说Python简单才开始学的,然后那段时间刚考完研,也没什么事,就多少瞅了瞅,然后发现语法的确简单很多,或者说简洁更合适. 当时看的是简明Python教程,没用多 ...
- Linux ip
工具/原料 linux系统 putty 方法/步骤 Linux下查看IP一般都是用命令在终端查看了,使用命令行来进行查看. 想要在图形界面查看的朋友也有办法,不过就比较复杂,不如一条命令来得痛 ...
- 转-ArcGIS Engine许可初始化
关于初始化Engine许可的,其实原理都很简单,大家一般都没有问题,但又往往会因为不够细心加上Engine的“小脾气”,让不少程序员都要在这里犯错. 以Engine9.2为例,应用程序是强制初始化许可 ...
- Nancy总结(三)Nancy资料介绍
Nancy 是一个轻量级用于构建基于 HTTP 的 Web 服务,可以基于 .NET 和 Mono 平台构建轻量级基于 HTTP 的Web 服务.它更多的是借鉴了Ruby的一些特性和Ruby的MVC ...
- hibernate的集中持久化方法的区别
一.预备知识 在所有之前,说明一下,对于hibernate,它的对象有三种状态,transient.persistent.detached 下边是常见的翻译办法: transient:瞬态或者自由态 ...
- 浅谈Android下的Bitmap之大Bitmap加载
引言 我们常常提到的“Android程序优化”,通常指的是性能和内存的优化,即:更快的响应速度,更低的内存占用.Android程序的性能和内存问题,大部分都和图片紧密相关,而图片的加载在很多情况下很用 ...
- 在ie浏览器,360浏览器下,margin:0 auto;不居中的原因
转自 http://blog.sina.com.cn/s/blog_6eef6bf60100nn4m.html margin:0 auto:不居中可能有以下两个的原因 没有设置宽度 看看上面的代码,根 ...
- PHP session
PHP Session PHP session 变量用于存储关于用户会话(session)的信息,或者更改用户会话(session)的设置.Session 变量存储单一用户的信息,并且对于应用程序中的 ...
- 关于BigDecimal 和 double 类型保存金钱,以及精度问题,银行家舍入法
1. BigDecimal 类型数据 的创建,构造函数 有 public BigDecimal(BigInteger intVal, long val, int scale, int prec); p ...
- 第3月第8天 RefCounted PlistBuddy
1.RefCounted引用计数 class Frame : public RefCounted<Frame> { // ... } http://www.cnblogs.com/dsky ...