黑马程序员_集合HashSet和hashCode算法

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

一、hashCode算法

       hashCode算法由来

       如果想查找一个集合中是否包含有某个对象,大概的程序代码怎样写呢?当发现某个元素与要查找的对对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则返回否定的信息。如果是一个集合中有很多元素,譬如有一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行啄一的比较才能得到结论,这样的效率太低。

       有人发明了一种hashCode算法,来提高查找的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域。

二、HashSet集合

       1,存储原则

        HashSet就是采用哈希算法存储对象的集合,为了保证一个类的实例对象能在HashSet正常存储,要求这个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等

       2,总结HashSet判断元素唯一性的依据

             2.1, hashCode算法得出的哈希码

             2.2,元素的equals()方法的比较结果

            HashSet就是采用哈希算法存储对象,判断元素唯一性的依据是哈希码和equals()方法比较的结果,因此向HashSet集合存储元素时,集合先判断元素的哈希码,如果元素的哈希码相同,接着调用元素的equals()方法,如果返回值为真,集合就会判定为元素是同一个元素。

       3,程序代码演示       

             3.1,定义一个Person类

class Person
{
	private String name;
	private int age;

	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public void setName(String name)
	{
		this.name=name;
	}
	public void setAge(int age)
	{
		this.age=age;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public int hashCode()
	{
		return name.hashCode()+age*12;
	}
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			return false;
		Person p=(Person)obj;
		return this.name.equals(p.name)&&this.age==p.age;
	}
}

              3.2,定义HashSet集合,存储Person对象

import java.util.*;
class HashSetTest 
{
	
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void getMyElements(HashSet hs)
	{
		Iterator it = hs.iterator();
		while(it.hasNext())
		{
			Object obj = it.next();
			Person p = (Person)obj;
			sop(p.getName()+":"+p.getAge());
		}
	}
	public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		hs.add(new Person("a1",11));
		hs.add(new Person("a1",11));
		System.out.println(hs.size());
	}
}

              3.3,程序分析

                       3.3.1,程序打印结果为1,说明集合只存进区一个元素,原因是根据Person类定义的hashCode和equals方法判断存入集合的Person的两个实例对象的哈希码相同并且equals方法返回的结果是true因此判定为同一个元素。

                       3.3.2,将Person类中的hashCode方法注释掉,则打印的结果为2,此时equals方法判断仍然是true,原因是Person类中的hashCode方法获取默认的哈希码,而默认的哈希码是根据内存地址算出来的,因而导致hashCode得出的哈希码不同,因此集合判断为两个不同元素被存进来。

                       3.3.3,将Person类中的hashCode做如下修改进行元素存储,打印结果不同,打印1的原因hashCode的返回值都相同为111,equals方法判断仍然是true,因而认定为同一个元素,打印2的原因hashCode的返回值都相同为111,equals方法判断仍然是false,被认定为两个不同的元素。

public int hashCode()
	{
		return 111;
	}
public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		hs.add(new Person("a1",11));
		hs.add(new Person("a1",11));
		System.out.println(hs.size());//打印结果:1
	}
public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		hs.add(new Person("a1",11));
		hs.add(new Person("a1",12));
		System.out.println(hs.size());//打印结果:2
	}

三、HashSet内存泄露问题

       当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在cantains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果。这也会导致从HashSet集合中单独删除当前对象,从而造成内存泄露。

       1,程序代码演示

             1.1,定义Persoon类

class Person
{
	private String name;
	private int age;

	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	public void setName(String name)
	{
		this.name=name;
	}
	public void setAge(int age)
	{
		this.age=age;
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public int hashCode()
	{
		return name.hashCode()+age*12;
	}
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			return false;
		Person p=(Person)obj;
		return this.name.equals(p.name)&&this.age==p.age;
	}
}

             1.2,定义集合存入Person对象

public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		Person p1 = new Person("a1",11);
		Person p2 = new Person("a2",12);
		hs.add(p1);
		hs.add(p2);
        	System.out.println(hs.size());//打印结果:2
		hs.remove(p1);
		System.out.println(hs.size());//打印结果:1
	}
}
public static void main(String[] args) 
	{
		HashSet hs = new HashSet();
		Person p1 = new Person("a1",11);
		Person p2 = new Person("a2",12);
		hs.add(p1);
		hs.add(p2);
       		System.out.println(hs.size());//打印结果:2
		p1.setAge(13);
		hs.remove(p1);
		System.out.println(hs.size());//打印结果:2
	}
}

          代码块一,元素存进集合后没有对属性进行修改,当集合使用remove(p1)时将p1元素移除,集合找到p1并将其移除打印为1

        代码块二,元素存进集合后p1对属性进行修改,当集合使用remove(p1)时将p1元素移除,打印结果仍为2,说明p1并为被移除,原因是p1对属性进行修改后其哈希码改变了,集合拿着改变后的哈希码去查找原来存储在集合中的p1,因为哈希码不同,集合将不会在原来p1对应的哈希码值所在的区域查找,因而就不会找到p1,导致实际并未将p1删除的结果。
三、HashSet集合总结

       1,存入HashSet集合的元素应复写hashCode和equals方法,并保持二者结果一致,即equals判断元素相同时,对应的hashCode应相同。
         2,存入HashSet集合的元素不要修改元素的属性,避免出现内存泄露。

  

 

         

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

详细请查看:<a href="http://edu.csdn.net" target="blank">http://edu.csdn.net</a>        

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值