我想请问一下hashCode到底代表什么东西呢,对象的hashcode和内存地址有什么关系啊?package wukun;
public class Stu {
private String name;
private String sex;
public Stu(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
} package wukun;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class HashCodeTest {
public static void main(String[] args) {
// Set h1 = new HashSet();
// Set h2 = new HashSet();
//
// ArrayList a1 = new ArrayList();
// ArrayList a2 = new ArrayList();
//
//// a1.add(new String("abc"));
//// a2.add(new String("abc"));
//
// h1.add(a1);
// h2.add(a2);
//
// System.out.println(h1.hashCode());
// System.out.println(h2.hashCode());
//
Stu s1 = new Stu("wukun","男");
Stu s2 = new Stu("wukun","男");
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
}
} 为什么2个ArrayList的hash码一样,而2个自定义的stu对象的hash码就不一样呢?(主要问这个问题)
以下内容说明了为什么要重写equals和hashCode方法,供大家参考。
这是jdk1.5中hashmap中put方法的源代码,
public V put(K key, V value) {
K k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table; e != null; e = e.next) {
if (e.hash == hash && eq(k, e.key)) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, k, value, i);
return null;
}hashset的底层实际上是hashmap,通过一个entry数组来是实现的,数组的每一个元素又是一个链表,eq(k, e.key)) 方法我看明白了,它的具体内容是这样的
static boolean eq(Object x, Object y) {
return x == y || x.equals(y);
}
这里的k是我们要存储的对象的引用,我错误的以为eq方法调用了object的equals方法,导致方法永远返回false,其实这里参数对象Object x虽然是Object的引用,但是它实际上指向我们外部一个实例化的子类,就是我们要往hashset中存的那个对象,这就很明显的告诉了我们为什么要重写equles方法了,当我们重写了equles方法以后,它就会去调用我们子类重写的那个方法,而不是调用Object的equels方法。
如果比较的结果相等,就代表hashset中已经存在了这个对象了,执行if语句里面的内容,这里的value好像没多大用处,它是一个没有内容的Object对象,但是当我们使用hashmap时,就是有value的,当保存一个key重复,但是value不重复的对象到hashmap中去的时候,这个key不变,而value被覆盖。当for循环结束走到尽头时,表示hashset中没有这个对象,addEntry(hash, k, value, i);就会执行,它的源代码如下:
这个方法很好理解,它相当与把新内容插到了table【i】的头节点,原来的头节点变成了第2个。void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
resize(2 * table.length);相当扩充容量,这个就不用解释了,我也没细看。
public class Stu {
private String name;
private String sex;
public Stu(String name, String sex) {
super();
this.name = name;
this.sex = sex;
}
} package wukun;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class HashCodeTest {
public static void main(String[] args) {
// Set h1 = new HashSet();
// Set h2 = new HashSet();
//
// ArrayList a1 = new ArrayList();
// ArrayList a2 = new ArrayList();
//
//// a1.add(new String("abc"));
//// a2.add(new String("abc"));
//
// h1.add(a1);
// h2.add(a2);
//
// System.out.println(h1.hashCode());
// System.out.println(h2.hashCode());
//
Stu s1 = new Stu("wukun","男");
Stu s2 = new Stu("wukun","男");
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
}
} 为什么2个ArrayList的hash码一样,而2个自定义的stu对象的hash码就不一样呢?(主要问这个问题)
以下内容说明了为什么要重写equals和hashCode方法,供大家参考。
这是jdk1.5中hashmap中put方法的源代码,
public V put(K key, V value) {
K k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table; e != null; e = e.next) {
if (e.hash == hash && eq(k, e.key)) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, k, value, i);
return null;
}hashset的底层实际上是hashmap,通过一个entry数组来是实现的,数组的每一个元素又是一个链表,eq(k, e.key)) 方法我看明白了,它的具体内容是这样的
static boolean eq(Object x, Object y) {
return x == y || x.equals(y);
}
这里的k是我们要存储的对象的引用,我错误的以为eq方法调用了object的equals方法,导致方法永远返回false,其实这里参数对象Object x虽然是Object的引用,但是它实际上指向我们外部一个实例化的子类,就是我们要往hashset中存的那个对象,这就很明显的告诉了我们为什么要重写equles方法了,当我们重写了equles方法以后,它就会去调用我们子类重写的那个方法,而不是调用Object的equels方法。
如果比较的结果相等,就代表hashset中已经存在了这个对象了,执行if语句里面的内容,这里的value好像没多大用处,它是一个没有内容的Object对象,但是当我们使用hashmap时,就是有value的,当保存一个key重复,但是value不重复的对象到hashmap中去的时候,这个key不变,而value被覆盖。当for循环结束走到尽头时,表示hashset中没有这个对象,addEntry(hash, k, value, i);就会执行,它的源代码如下:
这个方法很好理解,它相当与把新内容插到了table【i】的头节点,原来的头节点变成了第2个。void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
resize(2 * table.length);相当扩充容量,这个就不用解释了,我也没细看。
解决方案 »
- 求教:java 读取access数据出现乱码
- java socket问题
- Java 有序列表
- 如何把IE浏览器快速变成灰色来哀悼在汶川地震中遇难的同胞们!
- java 中Windowopened事件问题
- 一个难倒众多java高手的java难题征集答案!
- 制作bat文件运行java程序,怎么去掉后面的黑框啊?
- 请教高手极易回答的问题!!!急急急!!!
- 哪里有jbuilder7补丁3下,多谢了
- 怎样用weblogic的连接池?
- 关于强制转换对象有个几个好奇的问题想问下,请高手解答~~~
- 有一个整数n,写一个函数f(n),返回0到n之间出现的"1"的个数。比如f(13)=6,现在f(1)=1,问有哪些n能满足f(n)=n? 例如:f(13)=6, 因为1,2,3,4,5,6,7,8,9,10,11,12,13.数数1的个数,正好是6.
楼主【wnba1983】截止到2008-07-21 20:42:29的历史汇总数据(不包括此帖):
发帖的总数量:56 发帖的总分数:1670 每贴平均分数:29
回帖的总数量:54 得分贴总数量:5 回帖的得分率:9%
结贴的总数量:29 结贴的总分数:750
无满意结贴数:1 无满意结贴分:20
未结的帖子数:27 未结的总分数:920
结贴的百分比:51.79 % 结分的百分比:44.91 %
无满意结贴率:3.45 % 无满意结分率:2.67 %
楼主加油
当然hashcode就一样啦!
至于你Stu类没重写hashcode()方法,所以使用的是Object类的hashcode()方法,默认输出对象地址,两个不同对象的地址当然不一致
一般的集合都重写了hashCode方法,而当我们自定义对象的时候就要重写hasdCode方法,保证内容一样的对象算出相同的hash码hash码的用处只体现在需要散列的地方,入hashSet、hashMap,用户查找速度,其他地方它没有什么用,对吧呵呵,有种豁然开朗的感觉,谢谢你了
则2个对象equals则起散列码也必须相同
所以重写equals方法必须重写hashCode方法
重写hashCode方法的目的就是让2个对象散列码相同
为什么2个ArrayList的hash码一样,而2个自定义的stu对象的hash码就不一样呢?因为ArrayList的hashCode方法被重写了,重写的代码在ArrayList的父类AbstractList里面,重写的逻辑大概是以元素的hashCode为值来计算,而你ArrayList里装的元素又都是"abc"这个String,虽然是两个内容相同的String对象,但String对象的hashCode方法也被重写了,以内容为原始值来就算出来的,所以简而言之,相等的原因是:由于String类的hashCode以内容来计算,内容都是"abc",所以计算结果同,而ArrayList又以元素的hashCode来计算,而两个String元素的hashCode相等,所以计算结果也相等,最终导致两个ArrayList的hashCode也相等而两个Stu对象hashCode不相等是因为它们的hashCode继承自Object父类,这个类的hashCode是一个native方法(C语言实现的),实现逻辑由虚拟机内部产生,综述你可以重写任何对象的hashCode来保证实现你需要的逻辑,而且很明显你已经知道为什么要重写hashCode方法了,这很好!
每一个对象的hashCode 本身是不一样的!除非你重写 hashCode 方法!2个ArrayList的hash码一样,是因为 ArrayList 有重写hashCode 方法而且 你的ArrayList里面放置的对象 也重写了 hashCode方法! 是一样很正常!而2个自定义的stu对象的hash码就不一样呢?回答:每一个对象的hashCode 本身是不一样的!
说一下我自己的理解.在存放集合,这种数据的时候,我们可以选择List和Set两种形式,当然,Java当中它们不是具体的实现类.我们可以使用具体的实现类进行数据的存储.
但是List和Set的最显著区别,应该是,List可以放置相同的元素,Set只能放置不同的元素.也就是说Set里面的元素具有唯一性.当然ArrayList和LinkedList也只是具体的实现形式不同了.我们也可以实现一个ArraySet或LinkedSet.但是.我们会发现一个比较严重的问题.就是.当我们向ArraySet(或LinkedSet)里面添加元素的时候,我们要逐个比较里面的所有已有元素.这样,当集合中数量非常多的时候,比较次数也会直线上升.也就是说,当我们结合中有10000个元素的时候,我再添加一个元素,要首先比较最多10000次才能确定我要添加的元素是否已经存在了,这会严重影响集合的性能.为了更快捷的检索数据,我们才引进了hashCode的概念,每个Set里面的元素都会有一个hashCode的值,我们可以按照hashCode的值来存储集合里面的元素.如果要检索集合里面是否存在要添加的元素,只要先将该元素的hashCode值算出来,再到相应的位置进行查找,就可以了.对于相同hashCode的不同元素,我们把这个位置,按照链表的形式进行存放.这样,就可以很大程度上减少比较的次数.举个例子:我们Set集合里面可能已经10000个不同的元素了.当添加新元素的时候,我们根据新元素的hashCode值,找到相应的位置,这个位置所对应的链表里面,可能只有五个元素,那么,我们只要比较5次,就可以判断整个集合中是否已经存在该元素了,因为,相同元素的hashCode一定是相同的.呵呵.为了使根据hashCode确定位置的速度更快,我们采用数组的下标来表示位置,数组里面存放链表.下标都是自然数,所以,要把hashCode进行向数组下标的映射转换(其实就是与运算).最后,来谈谈,什么情况下重写hashCode,我们知道,自然情况下,hashCode所产生的值是很有规律的,这样的话,拥有10000个元素的Set,可能有9000个都在同一个位置上,这样,再加一个相同hashCode的元素时,那可能要比较90000次了.所以,我们要自己定义hashCode使得这些元素的hashCode在hashSet里面存储更加分散.不过Java的HashSet里面,已经有一个方法,将元素的hashCode做打散处理了,这个方法对于默认的hashCode还是比较有效果的.对于熟悉这些内容的程序员,为了程序的效率更高,可以重写hashCode方法.但是,不能使用随机数充当hashCode,每个元素,都应该对应唯一的一个hashCode,一个hashCode可以对应多个元素.
讲得有点晕了.
第四段:.对于相同hashCode的不同元素,我们把这个位置,
在Set里,会存在相同hashCode的不同元素?Set里元素的值不是不能重复么?会有相同的hashCode?
是不是可以这样理解: 在set.add()之前,会把要插入的值的hashcode计算出来,再到一段区域去找有没有equals的对吧? 意思就是不同的hashcode()是不可能会有相同的值对不对?还有一个问题:传说hash的查找速度是最快的就是根据hashcode去找到一段可能会有相等值的地方。 为什么能这样定位到那块需要查找的地方? 而在其它地方不会出现相同的值。 不太理解这一块。。哪位大虾讲下。不甚感激。。想了一天也想不通。 查找机制。
private int getSlot(CharSequence text) {
int code = getHashCode(text);
int pos = code & (entries.length-1);
char[] text2 = entries[pos];
if (text2 != null && !equals(text, text2)) {
final int inc = ((code>>8)+code)|1;
do {
code += inc;
pos = code & (entries.length-1);
text2 = entries[pos];
} while (text2 != null && !equals(text, text2));
}
return pos;
}
//// a1.add(new String("abc"));
//// a2.add(new String("abc"));
如果把第二个String对象换成不同的 比如“ab”,那结果哈希码就不一样了。