再次讨论散列,hashCode()
上个文章讲了我们通过hashCode()生成的结果作为桶的下表。从map我们大概看出一点端倪那就是在设计hashCode()时相同的值将产生相同的值。还有就是我们最常用的String具有不变性应该也是通过这种算法实现的(猜的)。如果我们给map中添加值时产生一个hashCode()值,取的时候产生的不同,那不乱套了。但是这个值一但变化就会产生不同的散列码。
此外hashCode依赖于具有唯一性的对象信息,因为hashCode()使用的是对象地址,如果不唯一那产生的值将会混乱保存,这个混论也不知道怎么将,后面有代码,看着代码好理解这个混乱。
想hashCode()实用,那必须快,并且有意义。有意义就是上面讲的基于对象的内容生成散列码。散列码不是独一无二的,但通过hashCode()和equals()可以完全确定对象身份。这是必须的。这又回到上面讲的了,通过hashcode生成桶,equals确定桶中的唯一元素。
好的hashCode()应该产生分布均匀的散列码。如果都集中到一起性能或许还不如线性查找快。
找了一个计算散列码的算法 result = 37 * result +c.后面代码会用到这个算法,当然你也可以自己写个好的。
下面的例子我们通过s和id这俩属性来获取一个桶。再通过这俩域找到对应的值。由于s相同id不同会让其产生不同的散列码,如果只用s大家试试,那么不同的对象可能会产生相同的值。
String不可变性的解释,下面是代码(产生相同的hashcode),有兴趣的自己重写个hashCode自己改变内部算法看看。
public static void main(String[] args) {
String i = "1";
String j = "1";
System.out.println(i.hashCode());//49
System.out.println(j.hashCode());//49
}
下面是个加深理解的代码
package a_rongqi.map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CountedString {
private static List<String> created = new ArrayList<String>();
private String s;
private int id = 0;
private CountedString(String str){
s = str;
created.add(s);
for (String s2 : created)
if (s2.equals(s))
id++;
}
public String toString(){
return "String" + s + "id:" + id + "hashCode()" + hashCode();
}
public int hashCode(){
int result = 17;
result = 37 * result + s.hashCode();
result = 37 * result + id;
return result;
}
public boolean equals(Object o){
return o instanceof CountedString && s.equals(((CountedString)o).s) && id == ((CountedString)o).id;
}
public static void main(String[] args) {
Map<CountedString,Integer> map = new HashMap<CountedString,Integer>();
CountedString[] cs = new CountedString[5];
for (int i = 0;i < cs.length;i++){
cs[i] = new CountedString("hi");
map.put(cs[i],1);
}
System.out.println(map);
for (CountedString cstring : cs
) {
System.out.println("Looking up" + cstring);
System.out.println(map.get(cstring));
}
}
}