黑马程序员——ArrayList&HashSet&Hashcode的学习总结

直观上来说,ArrayList不判断集合内的元素的重复性,HashSet会保持集合元素的唯一性,即如果有重复的元素,则后一个重复的元素就不会加入到HashSet集合里面,如下所示:

	public static void main(String[] args) {
		HashSet arr = new HashSet();
		// ArrayList arr = new ArrayList();
		Test33 test1 = new Test33(1, 1);
		Test33 test2 = new Test33(1, 1);
		Test33 test3 = new Test33(1, 2);
		arr.add(test1);
		arr.add(test2);
		arr.add(test3);
		arr.add(test1);
		System.out.println(arr.size());
	}
}

class Test33 {
	int x;
	int y;

	public Test33(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

}

如果用ArrayList集合来存放元素,最后的输出结果为4,如果用HashSet,输出结果为3。

这是因为在HashSet中,test1已经存在了,如果再用add()方法加入一个test1,则这个test1为重复对象,不会加入到集合中。


那HashSet是通过什么方法判断元素是否重复呢?是通过equals()方法。在没有重写equals()方法前,==和equeals()这两个比较的方法是一样的,都是比较内存的引用地址,如果引用地址都一样,则两个元素相同,对于HashSet集合来说,如果两个元素的equals()为true,则后一个元素不会被加入到集合中。当然,如果是一个一个的和集合中已有的元素进行比较,那效率就太低了,所以这里就用到了Hashcode,如下图所示:


HashSet把集合分成了不同的区域,每个区域内存放的是拥有相似hashcode的元素,如果有新元素需要加入集合,则只要在新元素对应的hashcode区域内找有没有重复元素即可,查找的效率自然就提高了。

如果想把值相同但引用不同的对象也当成是同一对象来看待的话,可以重写equals()方法,在eclipse中通过快捷生成方式覆盖equals()方法:

	public static void main(String[] args) {
		HashSet arr = new HashSet();
		// ArrayList arr = new ArrayList();
		Test33 test1 = new Test33(1, 1);
		Test33 test2 = new Test33(1, 1);
		Test33 test3 = new Test33(1, 2);
		arr.add(test1);
		arr.add(test2);
		arr.add(test3);
		arr.add(test1);
		System.out.println(arr.size());
	}
}

class Test33 {
	int x;
	int y;

	public Test33(int x, int y) {
		super();
		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;
		Test33 other = (Test33) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}

}

这样输出的结果就为2了,因为重写了equals()方法,test1和test2的x,y值相同,所以就被当成是同一元素,test2便不会加入到HashSet集合中。


这里还需要注意两个问题:

1:==和equals()的区别

上面已经说了,对应没有重写equals()的类,==和equals()比较的都是指向对象的内存引用地址,两者比较的结果是一样的。而String类默认重写了这个方法,用equals()比较两个字符串时是比较字符串的内容而非引用地址,所以,对于如下代码:

String s1 = new String("123");

String s2 = new String("123");

如果用==比较的话结果会是false,用equals()比较的话结果是true。但特别的:

String s1 = "123";

String s2 = "123";

这个无论是==还是equals()结果都是true,因为s1与s2分别指向由字符串常量”123” 创建的对象,在常量池中,只有一个对象,内容为123,有两个引用s1和s2指向这个对象,故这两个引用变量所指向的地址是相同的,所以无论是值比较还是引用比较都是true。


2:JAVA的内存泄漏问题

工作中接触到这个问题的时候会有一个疑问——JAVA有GC自动垃圾回收机制,不用的对象会自动销毁,内存空间也会被释放,那么还会出现内存泄漏的问题么?

答案是会的,而且正因为有了GC,所以JAVA的内存泄漏变得更隐蔽更难发现,连GC都不能回收这些引起泄漏的内存地址,程序员来找不就更麻烦了么?刚好对于HashSet这个集合,如果操作不当就会引起内存泄漏。

public static void main(String[] args) {
		HashSet arr = new HashSet();
		// ArrayList arr = new ArrayList();
		Test33 test1 = new Test33(1, 1);
		Test33 test2 = new Test33(2, 2);
		Test33 test3 = new Test33(3, 3);
		arr.add(test1);
		arr.add(test2);
		arr.add(test3);
		test1.x=2;//改变了test1中的x的值
		arr.remove(test1);//集合中删除test1
		System.out.println(arr.size());
	}
}


class Test33 {
	int x;
	int y;


	public Test33(int x, int y) {
		super();
		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;
		Test33 other = (Test33) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}






}



如上所示,重写了equals()方法,让其比较的是两个对象中x和y的数值而非引用地址,然后创建了三个不同的实例对象,x,y的值都不一样,加入到HashSet中,这时集合中有三个元素,size()的结果为3,然后把其中一个对象的x值改变,再在HashSet中把这个对象移除remove()掉,按理说这时size()的值应该为2,但实际运行结果为3,说明test1这个对象没有被删除,为什么呢?

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


根据类中重写的这个方法,hashcode的值用到了x和y的值,而x的值一旦改变了,hashcode的值肯定也改变,根据上面所说的,HashSet集合是按照不同的hashcode值分成不同的区域来存放元素,当一个元素的hashcode值改变后再移除这个元素,HashSet集合就会在这个元素的新的hashcode值所对应的区域中寻找,当然找不到这个元素,结果也就移除不了了。但这个元素还是实实在在的存在在HashSet集合中,存在在内存中,已经用不着了但又删除不掉,这边会造成内存的泄漏,而改变集合中某个元素的值的操作又是何其普遍,如果真的出现问题,GC机制肯定是帮不上忙,程序员要找问题所在也会陷入巨大的麻烦中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值