资料不少,多是照本宣科,读起来总觉的很生涩, 始终不能理解, 请高人用通俗的语言描述一下:
先谢谢了
先谢谢了
解决方案 »
- java程序加载皮肤包后,按钮边框去不掉
- 除开数据库,有什么办法对Java桌面应用的资源文件进行加密?
- java中线程同步的一个问题【synchronized】
- 求救?Exception in thread "main" java.lang.UnsupportedClassVersionError:
- 我没有SourceForge帐户,如何通过wincvs查看SourceForge上项目的代码呢?
- 多选:下列哪些方法与方法public void add(int a) {} 为合理的重载方法()
- 关于类关系问题,恳求答案
- 关于布局问题
- 如何得到本局域网上所有主机的ip地址及其主机名称?
- 这样的人能值多少钱呀?
- 所见所得(WYSIWYG ) 编辑器问题?
- 我是初学者 请问getInstance在抽象类中是咋用的?
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。 感觉这个描述的够清楚了。
每个对象都有一个hashcode,规定:
1、内容相同的对象hashcode肯定相等
2、内容不相同的对象hashcode可能相等也可能不相等所以如果两个对象的hashcode不相等则两个对象的内容肯定不相等,这样就不必一个一个去比较属性的值了,从而提高对象比较的速度。可以参看我BLOG中的《Java编程那些事儿》中关于Object类的介绍。
但是如果你new一个object作为key比如说说你Object o=new Object();o.hashcode()(其实这个就是我们说的那个key);这样我们用key去拿value的时候是拿不到的因为没new一次都可能得到一个不同的hashcode。所以我们要重写hashcode()这个方法,让new的时候这个hashcode不变。这样你永远可以通过这个key取到value。然后你要重写你的equals()方法,使内存中的内容也相等
"从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。" ,该整数是指?
为什么呢?
谢谢回答啊, 你的话是否可以这样理解:
1. 比较两个对象是否相等可以用equals(),也可以用hashcode(), 但后者的效率高
假设有两个对象a, b;
equals()的比较方式是:
a.equals(b), 如果结果为true则a,b内容相等;为false则不等
---
hashcode()的比较方式是否是:
a.hashcode() == b.hashcode(), 如果结果为true则a,b内容可能相等也可能不相等;为false则一定不等
那是否意味着用hoashcode()比较对象内容不彻底?,既只能判定内容不等的对象,但是对于hashcode相等的对象,则判定不了??
2,每个对象都有一个hashcode,但这个hashcode是何时产生的,保存在哪里, 比如:是在把一个对象放到集合中(是任意一个集合吗)时,由集合自动产生,然手作为对象的内建属性保存起来,还是只在通过显式调用对象的hashcode()方法时才产生这个编码? 3, hashcode主要用在集合中用于快速比较对象是否相等,这句话如何理解呢,是比较两个集合对象是否相等(因为比较集合相等需要比较集合中的对象是否相等)还是其它的? 4,“内容相同的对象hashcode肯定相等 ,内容不相同的对象hashcode可能相等也可能不相等 ” 这个是约定,如何理解“约定” 这个词呢,我的理解是既然是约定,可以遵照也可以不遵照, 如果不按照约定又会出现什么情况呢?先再谢谢啦
-----------
比较两个对象是否相等可以用equals(),也可以用hashcode(), 但后者的效率高
假设有两个对象a, b;
equals()的比较方式是:
a.equals(b), 如果结果为true则a,b内容相等;为false则不等
---
hashcode()的比较方式是否是:
a.hashcode() == b.hashcode(), 如果结果为true则a,b内容可能相等也可能不相等;为false则一定不等
那是否意味着用hoashcode()比较对象内容不彻底?,既只能判定内容不等的对象,但是对于hashcode相等的对象,则判定不了??
--------------比较两个对象是否相等
先比较两个对象的哈希码(当然是由对象的hashCode()方法得到)是否相等,如果哈希码不相等则我们就认为两个对象“不相等(注意这不是我们一般意义上的不相等,说白了就是不重复)”;而如果哈希码相等,则还要用equals()方法进行进一步判断:如果equals()比较的结果为true,则认为两个对象“相等(这是彻底相等了)”,如果equals()方法比较的结果为false,则认为两个对象“不相等(即认为不重复)”谢谢你们啦
觉得这位朋友说的很在理, 有几点想不通:
1.“hashmap是这样工作的:通过你传入的object的key去内存中找地址,当找到这个地址后在通过equals来比较这个地址中的内容是不是和你原来放进去的时候是一样的”
通过传入的object的key去内存中找地址, 这个地址是键的地址还是値的地址?
找到内存中的地址后,其内容可以获取,但如何记录原来放进去的key所对应的内容呢,它是通过哪种方式实现的,结构图是什么呢?”2.“(因为一个地址中可能会对应好几个内容,在hashcode没有被重写之前)。”
你说的这个地址是指内存地址还是其它什么地址? 如果是内存地址,一个地址可以对应几个内容该如何理解呢
3. “但是如果你new一个object作为key比如说说你Object o=new Object();o.hashcode()(其实这个就是我们说的那个key);这样我们用key去拿value的时候是拿不到的因为没new一次都可能得到一个不同的hashcode。所以我们要重写hashcode()这个方法,让new的时候这个hashcode不变。这样你永远可以通过这个key取到value。然后你要重写你的equals()方法,使内存中的内容也相等”
对该段不是太理解喔,汗, 你的意识是否是: 如果hashmap中存在一个Object类型的 key, 之后再new了一个Object对象出来, 这时的hashcode已经改变,进而取不到hashmap中的Object key所对应的値;
解决以上问题的方式是,在new Object()对象的时候,重写其hashcode()方法,让new的时候这个hashcode不变,为什么要再重写equals()方法呢?
看一下Object下equals的源代码:
public boolean equals(Object obj) {
return (this == obj);
}
也就是说在不重写equals()的情况下它比较的是对象是否相等比不是比较值是不是相等,所以如果我们不重写的话,如下面这样
Object p1=new Object();
Object p2=new Object();
p1和p2都是Object的引用也就是说p1.valuse=p2.valuse,但是如果不重写equals()的时候p1!=p2->p1.valuse!=p2.valuse。所以我们把它重写,让他不是比较对象是否相等而是比较值是不是相等。
一定要在每一个重载了equals的类中重载hashCode方法。不这样做会违背Object.hashCode的一般约定,并导致你的类与所有基于散列的集合一起作用时不能正常工作,这些集合包括HashMap、HashSet和Hashtable。 不重载hashCode方法违背了java.lang.Object的规范:相等的对象必须有相等的散列码。两个截然不同的实例根据类的equals方法也许逻辑上是等同的,但对于Object类的hashCode方法,它们就是两个对象,仅此而已。因而对象的hashCode方法返回两个看上去是随机的数值,而不是约定中要求的相等的值。
原帖地址:http://www.java3z.com/cwbwebhome/article/article2/2342.html?id=961还是不能系统的理解?哪位高人可否告知如果想系统完整深入的了解hashcode需要掌握哪些知识?
就好比 带有编号的猪圈里 有多少猪
这个编号就是hashcode
你要比较猪是否相同 通过hashcode(及猪圈编号)直接定位到一个猪圈
再到这个猪圈来找猪 肯定效率会提高
但是不知道原文在哪里了
Object p2=new Object();
p1和p2都是Object的引用也就是说p1.valuse=p2.valuse,但是如果不重写equals()的时候p1!=p2->p1.valuse!=p2.valuse。所以我们把它重写,让他不是比较对象是否相等而是比较值是不是相等。
-----------汗,还是不解, p1!=p2 -> p1.valuse!=p2.valuse。 他们之间有必然的联系么?p1.valuse 这个是什么? p1.valuse=p2.valuse 是赋值吗??
HashMap的put源代码是: 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;也就是说先比较hashcode值是否相等,相等的情况下,再进行判断equals方法是否返回true值,
因此hashCode()和equals(Object o)两个方法都要重写。
而get(Object o)方法的源代码: 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;
}可以看见hashCode()和equals(Object o)的调用。
-------------
找不到啊, 能告诉具体哪一章么?
--------2008-11-23 22:51Hashtable从JDK1.0就已经有了, 所以让我们先来看看它是怎么工作, 然后有浅入深, 来研究HashMap的原理, 以及两者的不同点.Hashtable有几个主要的字段, 如下,/**
* The hash table data.
*/
private transient Entry[] table;/**
* The total number of entries in the hash table.
*/
private transient int count;/**
* The table is rehashed when its size exceeds this threshold. (The
* value of this field is (int)(capacity * loadFactor).)
*
* @serial
*/
private int threshold;其中最重要的就是那个table数组了. 它就是整个hashtable的基本数据结构! 在来看一下这个字段
private transient Entry[] table;可以看到, hashtable的基本数据结构就是, 一个包涵Entry类的二维数组. 而这个Entry类是hashtable的内在类, 它其实是一个单向链, 让我们详细分析一下.
private static class Entry<K,V> implements Map.Entry<K,V> {
int hash;
K key;
V value;
Entry<K,V> next;
...
...看到这里有没有想到学校里教的数据结构原理这门课呢? Entry类就是定义了一个很简单的单向链结构, 它里面包括key, value和下个Entry类的对象next.
在这里我在强调一下, hashtable的数据结构就是一个包涵单向链的二维数组.
接下来让我们来看看hashtable的构造器是长的什么样的.最长用的public Hashtable() {
this(11, 0.75f);
}这个构造器调用了另外一个构造器public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)(initialCapacity * loadFactor);
}细读代码后, 我们发现这个构造器构造了table字段和threshold. Table前面已经详细讲了, 那么这个threshold又是什么东东呢?
其实这个threshold对hashtalbe的性能影响是很大的! 因为table是个数组, 如果在hashtable中保存的实体大于一定的数量后, 对数据的读写就会有很慢, 那是因为, 很多数据都保存在entry类的单向链中, 每次读写都要比对链中所有的数据, 链越长读写就越慢.
所以当数据容量大于threshold的时候, hashtable就会做rehash(), rehash把table的容量扩大一倍, 再把从前在table里的数据统统搬回新的table. 这样的一个过程, 开销是多么的大呀.
threshold = (int)(initialCapacity * loadFactor);
Hashtable类提供了构造涵数, 用户可以自定, intitialCapacity和loadFactor. 对于那些大概知道容量的hashtable, 用户应该自定intitialCapacity. 这样的话, 就可以省去一大笔rehash的开销.现在让我们来看hashtable的put和get操作
public synchronized V get(Object key) {
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}
return null;
}先来看get方法, get可谓是hashtable中的最基本方法了, 它是通过key来拿到hashtable中的value.
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
从key拿到hashCode, 从hashCode再计算出在table中的index, 也就是在数组中的第几个列.
至于为什么要与 0x7FFFFFFF, 那是hashtable 提供的hash算法, hashMap提供了不同的算法, 用户如果要定义自己的算法也是可以的. 如果要知道不同的具体算法, 就google or 百度一下吧.好了, 现在我们有了index, 就可以到table数组里的entry单向链去找value啦.for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return e.value;
}
}for语句就是简单的检索entry的单链, if语句检查key是否相同. 这里就遇到了java学习中的一个重大知识点. hasCode()和equal()的关系.
大家都学过如果hasCode()的值相同的话, equal不一定相同, 而如果equal相同的话, hasCode一定要相同. 但那是为什么呢? 其实答案就在上面的代码中!
Hashtable 的数据结构是一个包涵单向链的二维数组. 从hasCode我们得到hash和index, 并得以确定这个key在table数组中的第几个列, 然而这显然是不够的, 因为, entry类是一个单向列, 它可以是一个, 也可能是很多个key组成, 那么要从一系列有着相同hasCode的entry中找到, 我们所要的key的话, 就要用equals了. 只有两个key是相等的, 那才是我们要找的. 找到key之后, 只要简单的把value返回就好了. 如果对entry类还有疑问的话, 请参考前面的解释.public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
V old = e.value;
e.value = value;
return old;
}
}modCount++;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();tab = table;
index = (hash & 0x7FFFFFFF) % tab.length;
} // Creates the new entry.
Entry<K,V> e = tab[index];
tab[index] = new Entry<K,V>(hash, key, value, e);
count++;
return null;
}接下来再来看看put方法, 理解了get, put就很容易弄明白了.
首先, 要放入hashtable的value不能是null, 否则就报错.
其次, 然后要确保key不能已经在hashtable里面, 有的话, 就返回value.
再次, 检查是否容量已经太大, 如果太大话就rehash, 这会是一个很浪费资源的方法, 请参考前文.最后, 也是最重要的, 我们要把key-value保存到hashtable中去.Entry<K,V> e = tab[index];
tab[index] = new Entry<K,V>(hash, key, value, e);1.拿到当前在table数组中的entry对象.
2.根据传入的key和value建一个新的entry并赋予给当前的table的index中
protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
这是entry类的构造函数. 简单的说, 就是在单链的最前端加了个新的entry对象. 从这里也可以看出, 对于那些后写入的object, 反而可以以比较快的速度读出, 那是因为后写入的object, 总是在链的前端.看完了hashtable, 我们在来看看hashMap
hashMap可以算作是hashtable的升级版本, 最早从1.2开始有的.
整体上hashMap对hashtable类优化了代码. 比如说, 消除了hardcoding, 增加了code reuse等等.
但是, 两者之间最主要的不同有两点.
1.hashMap的读写是unsynchronized, 在多线程的环境中要注意使用
而hashtable是synchronized
这两者的不同是通过在读写方法上加synchronized关键字来实现的.hashMap
public V put(K key, V value)
public V get(Object key)hashtable
public synchronized V get(Object key)
public synchronized V put(K key, V value)可能有人问, 能synchronized, 能线程安全好啊. 为什么不要呢?
这里其实还是一个效率的问题. 对于线程安全的方法, 系统要进行加锁, 减锁操作. 性能会有很大的影响. 由于很多程序是在单线程或者说是线程安全的情况下工作的, 所以用synchronized就显得多余了.3.第二个不同是hashMap可以放空值, 而hashtable就会报错.
hashMap
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);hashtable
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
http://video.baidu.com/v?ct=301989888&rn=20&pn=0&db=0&s=25&word=%B9%FE%CF%A3%B1%ED%CA%FD%BE%DD%BD%E1%B9%B9
public boolean equals(Object obj) :指示其他某个对象是否与此对象“相等”。
equals 方法在非空对象引用上实现相等关系: 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
对于任何非空引用值 x,x.equals(null) 都应返回 false。
Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。 注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
public int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
hashCode 的常规协定是: 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)