一个粗心引起的思考

问题的起因是编写的以下代码今天出现了问题:

Java代码  收藏代码
  1. private List<Integer> ids = new ArrayList<Integer>();   //new a list to store user id 
  2. ...... 
  3. ids.add(user.getID());      //add user id to ids 
  4. ...... 
  5. ids.remove(user.getID());   //remove user id from ids 
private List<Integer> ids = new ArrayList<Integer>();	//new a list to store user id
......
ids.add(user.getID());   	//add user id to ids
......
ids.remove(user.getID());	//remove user id from ids

细心的朋友很容易就找到问题所在。因为List提供两个remove方法:

Java代码  收藏代码
  1. remove(int index)  
  2.  
  3. remove(Object o)  
remove(int index) 

remove(Object o) 

而我使用Integer作为泛型,这样调用remove方法时,编译器自动将Integer转化为了Int,所以实际调用的是remove(int index)这个方法。显然,我的本意是调用remove(Object o)。这样的问题,如果不是程序抛出IndexOutBoudsException,对于一向粗心的我还一直蒙在鼓里。

本文的重点在于解决此问题的过程中引发的一系列思考,并结合最近再次拜读《Effective Java》这本经典著作的结果给出了一些对java语言的心得体会。

回到问题本身来,最直接的解决方案如下:

Java代码  收藏代码
  1. ids.remove(Integer.valueOf(user.getID())); 
ids.remove(Integer.valueOf(user.getID()));

经过测试,该方案是可行的。

心血来潮,我提出了以下新的方案,它是否可行呢:

Java代码  收藏代码
  1. ids.remove(new Integer(user.getID())); 
ids.remove(new Integer(user.getID()));

初步分析,应该是不行的,因为传入的参数是一个新的Integer对象,而与原来的Integer是不相等的,所以remove操作将失败。

遗憾的是,以上分析是错误的,事实证明第二种方案也是可行的。为什么呢?

回想起《Effective Java》一书中,介绍"equals"方法的一段,有了启发。以上分析的结论是基于remove方法是以对象引用是否相等来判断,即通过“==”操作符来判断是否存在指定移除的对象。然而,它有没有可能是通过Integer对象的"equals"方法来判断的呢?如果是,两个不同的Integer对象,他们的值相同时,"equals"方法是否返回true呢?

验证第二个疑问很简单,写段程序验证一下就可。事实上,两个不同的Integer对象,他们的值相同时,"equals"方法是返回true的。因为Integer类override了Object.equals方法

验证第一个疑问也很简单,查看JDK源码:)

这时候,我突然想起了大学英语课文中关于爱因斯坦研究玩具鸟原理的文章(MS内容是这样的吧,大意是爱因斯坦想知道一只玩具鸟是怎么发出叫声的,而他一直不愿意拆开玩具来知道答案,直到最后他经过冥思苦想来得到答案时也没有拆开玩具鸟)当然,老爱是伟大的理论物理学家,咱只是个小Coder,没法比,只是好奇罢了。

扯远了,回到正题。可以通过以下测试来验证remove是否通过equals方法来判断的

Java代码  收藏代码
  1. private List<INT> INTs = new ArrayList<INT>(); 
  2.  
  3. public void testListINT() 
  4.     System.out.println("-----testListINT-----"); 
  5.     INTs.add(new INT(1)); 
  6.     INTs.add(new INT(2)); 
  7.     INTs.remove(new INT(1)); 
  8.     INTs.remove(new INT(2)); 
  9.     System.out.println("size="+INTs.size()); 
  10.     System.out.println("=====END====="); 
  11.  
  12. class INT 
  13.     public int i; 
  14.  
  15.     public INT(int i) { 
  16.         this.i = i; 
  17.     } 
  18.                } 
	private List<INT> INTs = new ArrayList<INT>();

	public void testListINT()
	{
		System.out.println("-----testListINT-----");
		INTs.add(new INT(1));
		INTs.add(new INT(2));
		INTs.remove(new INT(1));
		INTs.remove(new INT(2));
		System.out.println("size="+INTs.size());
		System.out.println("=====END=====");
	}

	class INT
	{
		public int i;

		public INT(int i) {
			this.i = i;
		}
                }

输出结果为:

-----testListINT-----
size=2
=====END=====

Object的equals方法,对于不同的对象返回的是false。在INT中override equals方法:

Java代码  收藏代码
  1. private List<INT> INTs = new ArrayList<INT>(); 
  2.  
  3. public void testListINT() 
  4.     System.out.println("-----testListINT-----"); 
  5.     INTs.add(new INT(1)); 
  6.     INTs.add(new INT(2)); 
  7.     INTs.remove(new INT(1)); 
  8.     INTs.remove(new INT(2)); 
  9.     System.out.println("size="+INTs.size()); 
  10.     System.out.println("=====END====="); 
  11.  
  12. class INT 
  13.     public int i; 
  14.  
  15.     public INT(int i) { 
  16.         this.i = i; 
  17.     } 
  18.  
  19.     public boolean equals(Object arg0) { 
  20.         if(arg0 instanceof INT){ 
  21.             if(((INT)arg0).i == i) 
  22.                 return true
  23.         } 
  24.         return false
  25.     } 
  26.  
  27.                } 
	private List<INT> INTs = new ArrayList<INT>();

	public void testListINT()
	{
		System.out.println("-----testListINT-----");
		INTs.add(new INT(1));
		INTs.add(new INT(2));
		INTs.remove(new INT(1));
		INTs.remove(new INT(2));
		System.out.println("size="+INTs.size());
		System.out.println("=====END=====");
	}

	class INT
	{
		public int i;

		public INT(int i) {
			this.i = i;
		}

		public boolean equals(Object arg0) {
			if(arg0 instanceof INT){
				if(((INT)arg0).i == i)
					return true;
			}
			return false;
		}

                }

输出结果为:

-----testListINT-----
size=0
=====END=====

所以说,remove方法还是通过equals方法来判断指定的对象是否与列表中的对象相同。

《Effective Java》一书中还提到:

“在每个改写了equals方法的类中,你必须也要改写hashCode方法。如果不这样的话,就会违反Object.hashCode的通用约定,从而导致该类无法与所有基于hash的集合类结合在一起正常运作”

“相等的对象必须具有相等的hash code”

为了验证,再添加以下代码:

Java代码  收藏代码
  1. public void testMapINT() 
  2.     System.out.println("-----testMapINT-----"); 
  3.     INTmap.put(new INT(1), "1"); 
  4.     INTmap.put(new INT(2), "2"); 
  5.     INTmap.remove(new INT(1)); 
  6.     INTmap.remove(new INT(2)); 
  7.     System.out.println("size="+INTmap.size()); 
  8.     System.out.println("=====END====="); 
	public void testMapINT()
	{
		System.out.println("-----testMapINT-----");
		INTmap.put(new INT(1), "1");
		INTmap.put(new INT(2), "2");
		INTmap.remove(new INT(1));
		INTmap.remove(new INT(2));
		System.out.println("size="+INTmap.size());
		System.out.println("=====END=====");
	}

输出为:

-----testMapINT-----
size=2
=====END=====

而在INT中override hashCode方法之后:

Java代码  收藏代码
  1. class INT 
  2.     public int i; 
  3.  
  4.     public INT(int i) { 
  5.         this.i = i; 
  6.     } 
  7.  
  8.     public boolean equals(Object arg0) { 
  9.         if(arg0 instanceof INT){ 
  10.             if(((INT)arg0).i == i) 
  11.                 return true
  12.         } 
  13.         return false
  14.     } 
  15.  
  16.     public int hashCode() { 
  17.         return i; 
  18.     } 
  19.  
  20.                } 
	class INT
	{
		public int i;

		public INT(int i) {
			this.i = i;
		}

		public boolean equals(Object arg0) {
			if(arg0 instanceof INT){
				if(((INT)arg0).i == i)
					return true;
			}
			return false;
		}

		public int hashCode() {
			return i;
		}

                }

输出为:

-----testMapINT-----
size=0
=====END=====

总结本文,List中,通过equals方法来判断元素是否相同;Hash类型的Collection子类,如:HashMap等是通过hashCode的返回值来标示Key值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值