JAVA 容器--比较大小与比较重复(2)

承接上文:

 

Comparable接口:针对排序list

 

问题:上面的算法根据什么确定容器中对象的“大小”顺序?

所有可以“排序”的类都实现了java.lang.Comparable接口,Comparable接口中只有一个方法

Publicint compareTo(Object obj)

返回0:表示this==obj

返回正数:表示this>obj

返回负数:表示this<obj

实现了Comparable接口的类通过实现comparaTo方法从而确定该对象的排序方式


equals()与hashCode():保证不重复

 

由来:

 

    要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?这就是Object.equals方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次equals方法。这显然会大大降低效率。  

    于是,Java采用了哈希表的原理。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。

    这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。

 

规则:

 

      所以,Java对于eqauls方法和hashCode方法是这样规定的:


      1.如果两个对象相同,那么它们的hashCode值一定要相同;

      2.如果两个对象的hashCode相同,它们并不一定相同(这里说的对象相同指的是用eqauls方法比较)。如不按要求去做了,会发现相同的对象可以出现在Set集合中,同时,增加新元素的效率会大大下降。

      3.equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。

       

    在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了;

    在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时Hashcode()方法根据String类的重写代码的分析,也可知道hashcode()返回结果也会相等。

    以此类推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。

 

总结:

 

    hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。 hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。

    如果我们不重写这两个方法,将几乎不遇到任何问题,但是有的时候程序要求我们必须改变一些对象的默认实现。

 

举例说明:

public class TestString{
	public static void main(String[] args){
		String s1="Hello";
		String s2="World";
		String s3="Hello";
		System.out.println(s1 == s3);
		
		s1=new String("hello");
		s2=new String("hello");
		System.out.println(s1 == s2);
		System.out.println(s1.equals(s2));
		
		
		}
}



以上的正确执行是因为我们已经默认重写了String类的equals()方法和hashCode()方法。但是如果是Object类呢?

import java.util.HashSet;
import java.util.Set;

public class TestEquals1{
	public static void main(String[] args){
			Cat cl=new Cat(1,2,3);
			Cat c2=new Cat(1,2,3);
			System.out.println(cl==c2);
			System.out.println(cl.equals(c2));
			
		}
	}

class Cat{
		
		int color,height,weight;
		public Cat(int color,int height,int weight){
			
			this.color=color;
			this.height=height;
			this.weight=weight;
			}
		
}



毫无疑问,上面的程序将输出false,但是,事实上上面两个对象代表的是通过一个cat。真正的商业逻辑希望我们返回true。重写equals()方法!


public class TestEquals{
	public static void main(String[] args){
			Cat cl=new Cat(1,2,3);
			Cat c2=new Cat(1,2,3);
			System.out.println(cl==c2);
			System.out.println(cl.equals(c2));
		}
	}

class Cat{
		
		int color,height,weight;
		public Cat(int color,int height,int weight){
			
			this.color=color;
			this.height=height;
			this.weight=weight;
			}
		public boolean equals(Object obj){
			if(obj==null)return false;
			else{
				if(obj instanceof Cat){
					Cat c=(Cat)obj;
					if(c.color==this.color && c.height==this.height && c.weight==this.weight){
						return true;
						}
					}
				}
				return false;
			}
	}




So are we done?没有,让我们换一种测试方法来看看。


上面的程序输出的结果是两个。如果两个cat对象equals返回true,Set中应该只存储一个对象才对,而且System.out.println(cat.contains(newCat(1,2,3)));判断是否存在时,结果输出为false,如图:




那么问题在哪里呢?



我们忘掉了第二个重要的方法hashCode()。就像JDK的Javadoc中所说的一样,如果重写equals()方法必须要重写hashCode()方法。我们加上下面这个方法,程序将执行正确。


public int hashCode()
     {
      final int PRIME =31;
      int result = 1;
      result = PRIME * result ;
      return result;
}





再总结:


    以上这些都是针对容器来说的(如何判断set中不重复,如何判断list中的顺序),当然最主要的是解释为什么重写equals()方法必须要重写hashCode()方法的问题。

    根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode方法,它们仅仅是两个对象。因此,违反了“相等的对象必须具有相等的散列码”。

    所以只要重写了equals(),一定要重写hashCode,否则Hash表都会失效,工作不正常。即便你用equals方法比较得到两个对象是相等的结论那你也得不到相同的哈希码

 

    即如果cat类只重写了equals(),hashcode没有被重写,加入元素时使用的hashcode()是继承于set<-collection<-object的,所以计算的hashcode值不同,存储位置不同,则认为元素不相同,也就能明白为什么上面的判断是否存在(System.out.println(cat.contains(newCat(1,2,3))))时输入的结果为false了。

 






JAVA 容器--比较大小与比较重复(2),古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。