java中的“==”,equals()和hashcde的区别
1、 ==
java中的数据类型,可分为两类:
1、基本数据类型,也称原始数据类型
byte,short,char,int,long,float,double,boolean 他们 之间的比较,应用(==),比较的是他们的值。
2、引用类型(类i,接口,数组)
当他们用(==)进行比较的时候,比较的 是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的记过位true,否则比较后结果为false。
对象是放在堆中的,栈中存放的是对象的引用(地址)。由此可见“==”是对栈 中的值进行比较的。如果要比较堆中对象的内容是否想用,那么就要充血equals方法了。
1 public static void main(String[] args){ 2 int int1=12; 3 int int2=12; 4 Integer Integer1 = new integer(12); 5 Integer Integer2 =new Integer(12); 6 Integer Integer3 = new Integer(12); 7 Integer a1 =127; 8 Integer b1 = 127; 9 Integer a =128; 10 Integer b=128; 11 String s1 = "str"; 12 String s2 = "str"; 13 String str1 =new String(“str”); 14 String str2 =new String(“str”) 15 16 System.out,println(“int1==int2”+(int1==int2)); 17 System.out,println("int1==integer1+"(int1==integer1)); 18 System.out,println("Integer1==Integer2"+(Integer1==Integer2)); 19 System.out,println("Integer3==b1"+(Integer3==b1)); 20 System.out,println("a1==b1"+(a1==b1)); 21 System.out,println("a=b"+(a==b)); 22 System.out,println("s1==s2"+(s1==s2)); 23 System.out,println("s1==str1"+()s1==str1); 24 System.out,println("str1==str2"+(str1==str2)); 25 }
输出结果:
int1==int2 true
int1==Integer1;true//Innteger会自动拆箱为int,所以true;(当Integer与int进行==比较是,Integer会拆箱成一个int类型,所以还是相当于两个int型比较,这里的Integer,不管是直接赋值,还是new创建的对象,质押跟int比较就会拆箱为int类型,所以是相等的)
Integer1==Integer2; false//是不同的对象,在内存中的存放地址不同(这是两个对象类型,而且不会进行拆箱比较),所以为false
Integer3==b1; false//integer3是一个对象类型,而b1是一个常量他们存放内存的位置不一样,素以也不等.
a1==b1; //true
a==b; //false 这是因为Integer作为常量是,对于-128到127之间的数,会进行缓存,也即是说int a1=127时,在 范围之内,这个时候就存放在缓存中,挡再创建a时,java发现缓存中存在127这个数了,就直接取出来赋值给a,所以a1==b1.当超过范围就是 new integer()来new一个对象了,所以a,b都是new Integer(128)出来的变量,所以他们不等 .
s1==s2; true//java中唯一不需要new'既可以产生对象的途径 ,这种形式的赋值称为直接量,他被存放在一个叫字符串拘留池(常量池)的地方;而new创建的对象都放在堆上.
(什么是字符串拘留?当我们声明一个字符串String s="hello"时,JVM会先从常量池中查找又没哟值为"Hello"的对象,如果有,会把该对象赋给当前引用,也就是说原来的引用和现在的引用指向同一个对象;如果没有,则在拘留池中创建一个值为"Hello"的对象,如果下一次还有类似的语句,例如String str="Hello"时,又会指向"Hello"这个对象,以这种形式声明字符串,无论有多少个都指向同一个底线)
s1==str1;false// 以new这种形式创建的对象,每执行一次就生成一个新对象,是两个独立对象
str1==str2;false//
2、要是类中覆盖了equals方法,那么就u要根据具体的代码来是却抵inequals方法的作用了,覆盖后一般都是通过对象的内容是否想等来判断对象是否想等。下面对equals进行重写。
1 public boolean equals(Object anObject){ 2 if (this==anObject){ 3 return true; 4 } 5 if(anObject insrtanceof Striing){//instancef操纵检查符 6 String anotherString = (String)anObject; 7 int n = cont; 8 if(n==anotherString.count){ 9 char v1[] = values; 10 char v2[]=anotherString.value; 11 int i = offset; 12 int j = anotherString.value; 13 while (n--!=0){ 14 f(v1[I++]!=v2[j++]) 15 return false; 16 } 17 return true;} 18 } 19
20 retrn false;
}
即String中equals方法判断相等的步骤是:
1、若A==B即是同一个String对象,返回true;
2、若对比对象是String类型则继续,否则返回ufalse;
3、判断A、B长的是否一样,不一样的话返回false
4、逐个字符比较,若有不相等字符,返回false
这里对equals需要注意我五点:
1、自反性:对人一引用值x,x.equals(x)的返回值一定为true;
2、对称性:对于任何引用值x,y当且仅当y.equals(x)返回值为true时,x.equals(y)的返回值应为true;
3、传递性:如果.x.equals(y)=true,y.equals(z)=true,则x.equals(z)=true
4、一致性:若参与比较的对象没有任何改变,则对象比较的结果哦也不应有任何改变
5、非空性:任何非空的引用值x,x.equals(null)的返回值一定为false;
实现高质量equals方法的诀窍:
1、使用==符号检查“参数是否为这个对象的引用“。如果是,则返回true。这只不过是一种性能优化,如果比价操作有可能很昂贵,就值得 这么做。
2、使用instancef操作符检查”参数是否为正确的类型“。如果不是,则返回false。一般来说,所谓”正确的类型“是指equals方法所在的那个类。
3、把参数转换陈正确的类型。因为转换之前进行过instanceof测试,素偶一确保会成功。
4、对于该类中的每个”关键“域,检查参数中的域是否与该对象中对应的域相匹配。如果 这些 测试全部成功,则返回true,否则false;
5、当编写完成了equals方法之后,检查”对称性“,”传递性“,”一致性“。
3、hashCode
hashCode()方法返回的就是一个数值,生成一个hash码,主要用途就是对对象进行散列的时候作为key输入,hashCode方法实际上返回的就是对象存储的屋里地址(实际上并不是真正的内存的物理地址)
散列函数,散列算法,哈希函数是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数将任意长度的二进制值映射位较短的固定长度的二进制值,这个小的二进制值称为哈希值,好的散列函数在啊输入域中很少出现散列冲突。
所有散列函数都有如下一个基本特征:
1:如果a=b,则h(a)=h(b)。
2:如果a!=b,则h(a)与h(b)可能得到相同 的三列支散列值。
hashCode的作用:Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者无序,但元素不可重复。
要想保证袁术不重复,可拉ing个是否重复应该依据什么来判断,可以使用Object。equals方法,但是当元素很多的时候,后添加到集合中的元素比较的次数就非常多来,也就是说,如果集合中现在已经有2000个元素,那么第2001个元素加入集合是,他就要调用2000次equals方法,这显然会大大降低效率,所以Java采用来哈希表的原理。如此一来先吊用这个元素的hashCode方法,就一下子能定位倒他应该放置的屋里位置上;如果这个位置上没有元素,就直接存储在这个位置上,不用再进行任何比较;如果这个位置上已经有元素来,就调用它的equals方法与新元素进行比较,相同的话就不存,不相同就散列其他的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低来,几乎只需要一两次。
4、equals方法和hashCode方法关系
Java对于equals放大和hashCode方法是这样规定的:
(1)同一对象上对此调用hashCode()方法,总是返回相同的整型值。
(2)如果a.equals(b),则一定有a.hashCode()一定等于b.hashCode()。
(3)如果!a.equals(b),则a.hashCode()不一定等于b.hashCode()。此时如果a.hashCode()总是不等于b.hashCode(),会提高hashtables的性能。
(4)a.hashCode()==b.hahsCode(),则a.equals(b)可真可假。
(5)a.hashCode()!=b.hashCode(),则a.equals(b)为假
结论:
1、如果两个对象equals,java运行时环境会认为他们的hashCode一定想等。
2、如果两个对象不equals,他们的hashCode有可能想等。
3、如果两个对象hashCode想等,他们不一定equals。
4、如果两个对象hashCode不想等,他们一定不equals。
关于这两个方法的重要规范
规范一:若要重写equals(Object obj)方法,有必要重写hashCode()方法,确保通过equals(Object obj)方法判断结果为true的两个对象具备相等的hashCode()返回值,简单来说“如果两个对象相同,那么他们的hashCOde应该想等”。不过应该注意:这个只上规范,如果你非要写一个类让equals(Object obj)返回true而hashCode()返回两个不想等的值,编译和运行都是不会报错的,不过这样违反了Java规范,程序也就埋下了BUG
规范二:如果equals(Object obj)返回false,既两个对象”不相同“,并不要求对这两个对象调用hashCode()方法得到阿玲个不相同的数,也就是”如果两个对象不相同,他的hashCode可能相同“。
5、 总结:
1、equal方法用于比较对象的内容是否想等(覆盖以后)
2、hashCode方法只有在集合中用到
3、当覆盖了equals方法时,比较对象是否想等将通过覆盖后的equals方法进行比较(判断对象的内容是否想等)。
4、将对象放入到集合中是,首先判断要放入对象的hashCode值与集合中任意一个元素的hashCode值是否想等,如果不想等直接将该对象放入集合中,如果hashCode值想等,然后再通过equals方法判断equals方法判断要入对象与集合中的任意一个对象是否想等,如果equals判断不想等,直接将该元素放入到集合中,否则不放入。