java 翻盖hashCode()深入探讨 代码示例
package org.rui.collection2.hashcode; /** * 覆盖hashcode * 设计HashCode时最重要的因素 就是:无论何时,对同一个对象调用HashCode都应该产生同样的值, * 如果你的HashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化 时 * HashCode就会生成一个不同的散列码,相当于产生一个不同的健 * 此外 也不应该使HashCode依赖于具有唯一性的对象信息,尤其是使用this的值,这只能很糟糕, * 因为这样做无法生成一个新的健,使这与Put中原始的健值对中的健相同,它的默认的HashCode使用的是对象的地址 * 所以 应该 使用对象内有意义的识别信息 * * 以下以String类为例 String对象都 映射到同一块内存域, * 所以 new String("hello") 生成的两个实例 ,虽然是相互独立的, * 但是对它们使用HashCode应该生成同样的结果,以下示例可以看到 * 对String而言,HashCode明显是基于String的内容的, * * 因此 要想使HashCode实用,它必须 速度快,并且必须有意义。也就是说,它必须基于对象的内容生成的散列码, * 记得吗,散列码不必是独一无二的 (应该更关注生成速度,而不是唯一性) * 但HashCode和Equals 必须能够完全确定对象的身份 * 所以散列码生成的范围并不重要,只要是int即可 * 还有别一个影响因素,好的HashCode应该产生分布均匀的散列码 * * * @author lenovo * */ public class StringHashCode { public static void main(String[] args) { String[] hello="Hello Hello".split(" "); System.out.println(hello[0].hashCode()); System.out.println(hello[1].hashCode()); } } /**output: 69609650 69609650 */
package org.rui.collection2.hashcode; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 覆盖hashcode * * 在Effective java programming language guide ....这本书 为怎样写出一份像样的hashcode给出了基本指导 * 1,int变量result 赋予某个非零值 常 量, * 2,为对象内每个有意义的域f(即每个可以做equlas操作的域) 计算一个int散列码c * ----------------------------------------- ----------------------------------------- * 域类型 计算 * ----------------------------------------- ----------------------------------------- * boolean c=(f?0:1) * byte,char,shor或int c=(int)f * long c=(int)(f^(f>>>32)) * float c=Float.floatToIntBits(f); 根据 IEEE 754 浮点“单一格式”位布局,返回指定浮点值的表示形式 * double c=Double.doubleToLongBits(f); * Object 其equals调用这个域的equlas c=f.hashCode(); * 数组 对每个元素应用上述规则 * * 3,合并计算得到的散列码 * 4,返加result * 5检查hashcode最后生的结果。确保相同的地象有相同的散列码 * ----------------------------------------- ----------------------------------------- * * * CountedString是由一个String和id组成,此id代表包含相同String的CountedString对象的编号 * 所有的String被存储在ArrayList中,在构造器中通过迭代遍历此ArrayList完成对Id的计算 * * hashcode and equals都是基于CountedString的两 个域来生成的结果,如果它们只是基于String或只基于Id * 不同的对象就可能产生相同的值 * * 在Main中使用了相同的String,创建了多个CountedString对象,这说明,虽然String相同 , * 但由于Id不同,所以使得它们的散列码并不相同, * @author lenovo * */ public class CountedString { private static List<String> created = new ArrayList<String>(); private String s; private int id = 0; public CountedString(String str) { s = str; created.add(s); // id is the total numbe of instances // of this string in by CountedString for (String s2 : created) { if (s2.equals(s)) id++; } } public String toString() { return "String:" + s + " id:" + id + " hashCode:" + hashCode(); } public int hashCode() { //the very simple approach //return s.hashCode()*id; //using joshua bloch's recipe//使用joshua bloch的配方 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], i);//autobox int->Integer } System.out.println(map); for(CountedString cstring:cs) { System.out.println("Looking up"+cstring); System.out.println("map.get(cstring):"+map.get(cstring)); } } }
package org.rui.collection2.hashcode; /** * 覆盖hashCode * * compareTo 方法有一个比较结构,因此它会产生一个排序序列,排序的规则首先按照实际类型排序 * 然后如果有名字的话,按照name排序,最后按照创建的顺序排序, * @author lenovo * */ public class Individual implements Comparable<Individual>{ private static long counter=0; private final long id=counter++; private String name; public Individual(String name){this.name=name;} public Individual(){} public String toString(){ return getClass().getSimpleName()+ (name==null?"":" "+name); } public long id(){return id;} public boolean equals(Object o){ return o instanceof Individual&& id==((Individual)o).id; } public int hashCode(){ int result=17; if(name!=null) result=37*result+name.hashCode(); result=37*result+(int)id; return result; } @Override public int compareTo(Individual o) { String first=getClass().getSimpleName(); String argFirst=o.getClass().getSimpleName(); int firstCompare=first.compareTo(argFirst); if(firstCompare!=0) { return firstCompare; } if(name!=null && o.name!=null) { int secondCompare=name.compareTo(name); if(secondCompare!=0) return secondCompare; } return (o.id<id?-1:(o.id==id?0:1)); } }
package org.rui.collection2.hashcode; import java.util.*; import org.rui.classts.Pet; import org.rui.classts.chilnd.*; /** * * 下面的示例说明了它如何工作的; * * 由于所有的寵物都有名字,因此它們首先按照類型排序,然后在同類型中按照 名字排序 * 為新類編寫正確的hashCode和equals很需要技巧,Apache的jakarta commons項目中有許多工具可以人幫助你完成此事 * @author lenovo * */ public class IndvidualTest { public static void main(String[] args) { //Set<Individual> pets=new TreeSet<Individual>(); Pet p=new Cat("猫"); Pet p1=new Dog("狗"); Pet p2=new EgyptianMan("EgyptianMan"); Pet p3=new Manx("马恩岛猫"); Pet p4=new Pug("巴哥犬"); //一个人有很多宠物 Map<Individual,List<? extends Pet>> list2=new HashMap<Individual,List<? extends Pet>>(); Individual in=new Individual("Dawn"); Individual in2=new Individual("東方不敗"); list2.put(in, Arrays.asList(p,p1,p2,p3,p4)); list2.put(in2, Arrays.asList(p2,p3,p4)); System.out.println(list2);//輸出這個人的寵物 //查找Dawn的寵物 List<? extends Pet> l=list2.get(in); System.out.println(l); } } /**output: {Individual 東方不敗=[Pet [name=EgyptianMan], Pet [name=马恩岛猫], Pet [name=巴哥犬]], Individual Dawn=[Pet [name=猫], Pet [name=狗], Pet [name=EgyptianMan], Pet [name=马恩岛猫], Pet [name=巴哥犬]]} [Pet [name=猫], Pet [name=狗], Pet [name=EgyptianMan], Pet [name=马恩岛猫], Pet [name=巴哥犬]] */