Java基础知识_ArrayList和HashSet的比较,以及HashCode方法

今天学了一个细节上的内容,那就是ArrayList和HashSet的比较以及Hashcode方法。

通过之前集合框架的学习,我们大致了解了两种集合的区别。List集合的存放是有顺序的,也就是说我们可以指定位置存放或者取出元素,集合中的元素可以重复。而Set集合的存放是无序的,或者说他的存放顺序并非我们指定,且其中的元素不可以重复。

OK,我们先来看看下面的例子:
首先我们有一个用于测试的类ReflectPoint
package cn.icecino.d1;
public class ReflectPoint {
	
	public int x;
	private int y;
	
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}		
}

接着我们创建它的实例对象并放入ArrayList中
package cn.icecino.d1;

import java.util.ArrayList;
import java.util.Collection;

public class ReflectTest {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		
		Collection collection = new ArrayList();
		
		ReflectPoint p1 = new ReflectPoint(3,3);
		ReflectPoint p2 = new ReflectPoint(5,5);
		ReflectPoint p3 = new ReflectPoint(3,3);
		
		collection.add(p1);
		collection.add(p2);
		collection.add(p3);
		collection.add(p1);
		
		System.out.println(collection.size());
	
	}
}


输出的结果是几呢?——是4 。
因为ArrayList是允许重复的,所以四个add语句每一个都放入到了集合中。

那么如果我们将上面的ArrayList换成HashSet呢?
输出结果是——3 。
因为最后一个p1没有放到集合里去,编译器在将对象放入集合之前会首先检测一下集合中是否有同样的元素,如果有则不再放入(记住不是覆盖,而是没有放进去)。

但是我们注意到了,在我们创建对象时,p3的初始化值和p1是一样的,那么为什么p3放进集合中去了呢?
因为p1和p3在底层调用比较方法的时候,equals比较的是Hashcode值,而Hashcode值是由内存在计算。
如果我们希望p3和p1相等的话,就需要我们在ReflectPoint中覆盖equals方法和Hashcode方法,就像这样:
package cn.icecino.day1;

public class ReflectPoint {
	public int x;
	private int y;
	
	public ReflectPoint(int x,int y){
		this.x = x;
		this.y = y;
	}


	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}


	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
	
}

这时HashSet集合中的元素数则是我们希望的那样,为2 。


现在我们可以在做一些变动——如果我们只覆盖了equals方法,而没有覆盖Hashcode方法呢?结果会是什么?2还是3?
结果是——看运气吧!也许是2,也许是3.


咦,这就很奇怪了不是么,为什么要看运气呢?


想要回答这个问题,就得先了解Hashcode方法。


如果想要查找一个集合中是否包含有某个对象,大概的程序代码怎样写呢?你通常是逐一取出每个元素与要查找的对象进行比较,当发现某个元素与要查找的对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则,返回都否定的信息。如果一个集合中有很多个元素,譬如有一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行逐一比较才能得到结论。有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域。
 

现在你应该明白了为什么上面的输出结果是2还是3要靠运气,因为你也不知道p1和p3在不在同一个区域中,如果两者的哈希码在同一区域,那么p3就无法放入集合中,反之p3就可以放入集合中。


所以,在实际编程中,我们一般会同时覆盖equals和Hashcode方法以保持他们的一致性——当然,如果你的程序不需要用到Hashcode,你完全没必要去覆盖它。


这里还有个需要我们注意的小问题:
当一个对象被存储进集合中以后,就不要再修改对象中那些参与了哈希值计算的字段了,否则,对象修改后的哈希值,和已经存入HashSet集合的哈希值便不一样了,这个时候,即使使用当前引用的contains方法去HashSet集合中寻找之前被你存入的对象,也将获得无法找到返回的结果,从而造成 内存泄漏

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值