黑马程序员:Java基础——Set集合之HashSet

------- Java EE培训java培训、期待与您交流! ----------

1.HashSet概念

|--Set:元素师无序的,元素不可以重复,该集合无索引。
     |--HashSet:底层数据结构是哈希表。
class Demo{}
class HashSetDemo{
    public  static void main(String [] args){
        Demo d1 = new Demo();
        Demo d2 = new Demo();
        sop(d1);
        sop(d2);
    }
    public static void sop(Object obj){
        System.out.println(obj);
    }
}
输出的即为d1和d2的内存地址,也就是哈希值。

下图是HashSet存储机制:

接下来,我们试用一下HashSet,通过查看API文档,我们看到没有任何独特的或是新的没有接触过的方法,所以我们按老方法,新建一个HashSet并将其遍历出来:

public static void sop(Object obj){
    System.out.println(obj);
}
	
public static void main(String[] args) {
    HashSet hs = new HashSet();
    hs.add("Java01");
    hs.add("Java02");
    hs.add("Java03");
    hs.add("Java04");
		
    for(Iterator it = hs.iterator();it.hasNext();){
        sop(it.next());
   }
}

运行结果则是:

Java02
Java03
Java04
Java01
可以看到,不是按我们原有的顺序储存的。

2.HashSet存储自定义对象

我们还以例子说明:将自定义对象作为元素存到HashSet集合中,并去除重复元素。
例如:存人对象,同姓名同年龄视为同一人,为重复元素。依照ArrayList集合的思想,我们写出了一下代码:

import java.util.HashSet;
import java.util.Iterator;

public class HashSetTest {
    public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add(new Person("ZhangSan",21));
		hs.add(new Person("LiSi",22));
		hs.add(new Person("WangWu",23));
		hs.add(new Person("LiSi",22));
		
		for(Iterator it = hs.iterator();it.hasNext();){
			Person p = (Person) it.next();
			sop("Name:"+p.getsName()+",Age:"+p.getiAge());
		}
	}
	
	public static void sop(Object obj){
    	System.out.println(obj);
    }
}

class Person{
	private String sName;
	private int iAge;
	Person(){}
	Person(String sName,int iAge){
		this.sName = sName;
		this.iAge = iAge;
	}
	
	public boolean equals(Object obj){
		if(!(obj instanceof Person)){
			return false;
		}else{
			Person p = (Person) obj;
                        System.out.println("this.sName="+this.sName+"--p.sName="+p.sName);
			return this.sName.equals(p.sName)&&this.iAge==p.iAge;
		}
		
	}
	
	public String getsName() {
		return sName;
	}
	public void setsName(String sName) {
		this.sName = sName;
	}
	public int getiAge() {
		return iAge;
	}
	public void setiAge(int iAge) {
		this.iAge = iAge;
	}
}

但是,运行结果很不乐观:

Name:WangWu,Age:23
Name:ZhangSan,Age:21
Name:LiSi,Age:22
Name:LiSi,Age:22

我们看到我们的equals方法并没有被调用,这是为什么呢?

因为,我们添加的元素都是new出来的,所带的Hash值都是不一样的,地址不一样,HashSet便认为它们是不一样的。

那么,我们需要重写Object的hashCode()方法。

public int hashCode(){
	return 60;
}


当我们运行的时候发现,重复的去掉了,equals方法也被调用了。以下是运行结果

this.sName=LiSi--p.sName=ZhangSan
this.sName=WangWu--p.sName=ZhangSan
this.sName=WangWu--p.sName=LiSi
this.sName=LiSi--p.sName=ZhangSan
this.sName=LiSi--p.sName=LiSi
Name:ZhangSan,Age:21去
Name:LiSi,Age:22
Name:WangWu,Age:23
在Set集合中,重复元素就无法被存进来。这时,我们去掉了重复的,可是,这样每存一个就判断一次,效率是比较低的。

我们不妨用元素的内容,比如:Person的姓名+年龄得到的值作为Hash值,如果不一样,再进行equals判断。

public int hashCode(){
	System.out.println(this.sName+"...hashCode"+sName.hashCode());
	return sName.hashCode()+iAge;
}

我们编译运行来看看结果:

ZhangSan...hashCode-1367991180
LiSi...hashCode2367699
WangWu...hashCode-1711270815
LiSi...hashCode2367699
this.sName=LiSi--p.sName=LiSi
Name:WangWu,Age:23
Name:LiSi,Age:22
Name:ZhangSan,Age:21


我们发现,当后面LiSi的HashCode值和前面LiSi的HashCode值相同时去判断比较。确认重复,就没有被存入集合中。

然而,即便是这样也会出现漏洞,例如l+21正好与Z+15的HasCode值相同,就会出现重复比较的现象,针对这个,我们在iAge乘以一个随机的数。

return sName.hashCode()+iAge*2;


运行一次,看看结果:

ZhangSan...hashCode-1367991180
LiSi...hashCode2367699
WangWu...hashCode-1711270815
LiSi...hashCode2367699
this.sName=LiSi--p.sName=LiSi
Name:ZhangSan,Age:21
Name:LiSi,Age:22
Name:WangWu,Age:23

以下是修正完的全部代码:

import java.util.HashSet;
import java.util.Iterator;

public class HashSetTest {
    public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add(new Person("ZhangSan",21));
		hs.add(new Person("LiSi",22));
		hs.add(new Person("WangWu",23));
		hs.add(new Person("LiSi",22));
		
		for(Iterator it = hs.iterator();it.hasNext();){
			Person p = (Person) it.next();
			sop("Name:"+p.getsName()+",Age:"+p.getiAge());
		}
	}
	
    public static void sop(Object obj){
    	System.out.println(obj);
    }
}

class Person{
	private String sName;
	private int iAge;
	Person(){}
	Person(String sName,int iAge){
		this.sName = sName;
		this.iAge = iAge;
	}
	
	public int hashCode(){
		//System.out.println(this.sName+"...hashCode"+sName.hashCode());
		return sName.hashCode()+iAge*2;
	}
	
	public boolean equals(Object obj){
		if(!(obj instanceof Person)){
			return false;
		}else{
			Person p = (Person) obj;
			//System.out.println("this.sName="+this.sName+"--p.sName="+p.sName);
			return this.sName.equals(p.sName)&&this.iAge==p.iAge;
		}
		
	}

最后,我们来总结一下:

HashSet是如何保证元素唯一性呢?

是通过元素的两个方法,hashCode和equals来完成。

如果元素的HashCode值相同,才会判断equals是否为true。

如果元素的HashCode志不同,不会调用equals。

3.HashSet判断和删除的依据

基于上一节练习,我们来对元素来进行删除,为了便于观看,我的代码如下:

System.out.print("判断“ZhangSan,21”是否存在-----");
if(hs.contains(new Person("ZhangSan",21))){
	sop("存在");
}else{
	sop("不存在");
}

运行结果为:

判断“ZhangSan,21”是否存在-----存在

我们把上节注释掉的打印取消掉,运行结果如下:

ZhangSan...hashCode-1367991180
LiSi...hashCode2367699
WangWu...hashCode-1711270815
判断“ZhangSan,21”是否存在-----ZhangSan...hashCode-1367991180
this.sName=ZhangSan--p.sName=ZhangSan
存在
Name:ZhangSan,Age:21
Name:LiSi,Age:22
Name:WangWu,Age:23

我们发现程序拿到了ZhangSan这个对象,拿去和集合中的元素作对比,结果是存在的,于是输出存在。

我们再来试一试删除数据,将刚刚的判断代码注释掉。

这是删除元素的代码:

hs.remove(new Person("LiSi",22));


我们来运行看看:

ZhangSan...hashCode-1367991180
LiSi...hashCode2367699
WangWu...hashCode-1711270815
LiSi...hashCode2367699
this.sName=LiSi--p.sName=LiSi
Name:ZhangSan,Age:21
Name:WangWu,Age:23
我们发现,LiSi,22这个元素被我们删除掉了。

通过总结,我们发现对于判断元素是否存在,以及删除等操作依赖的方法也是元素的hashCode和equals方法。

以下是全部代码:

import java.util.HashSet;
import java.util.Iterator;

public class HashSetTest {
    public static void main(String[] args) {
		HashSet hs = new HashSet();
		hs.add(new Person("ZhangSan",21));
		hs.add(new Person("LiSi",22));
		hs.add(new Person("WangWu",23));
		//hs.add(new Person("LiSi",22));
		
		/*System.out.print("判断“ZhangSan,21”是否存在-----");
		if(hs.contains(new Person("ZhangSan",21))){
			sop("存在");
		}else{
			sop("不存在");
		}*/
		
		hs.remove(new Person("LiSi",22));
		
		for(Iterator it = hs.iterator();it.hasNext();){
			Person p = (Person) it.next();
			sop("Name:"+p.getsName()+",Age:"+p.getiAge());
		}
	}
	
	public static void sop(Object obj){
    	System.out.println(obj);
    }
}

class Person{
	private String sName;
	private int iAge;
	Person(){}
	Person(String sName,int iAge){
		this.sName = sName;
		this.iAge = iAge;
	}
	
	public int hashCode(){
		System.out.println(this.sName+"...hashCode"+sName.hashCode());
		return sName.hashCode()+iAge*2;
	}
	
	public boolean equals(Object obj){
		if(!(obj instanceof Person)){
			return false;
		}else{
			Person p = (Person) obj;
			System.out.println("this.sName="+this.sName+"--p.sName="+p.sName);
			return this.sName.equals(p.sName)&&this.iAge==p.iAge;
		}
		
	}
	
	public String getsName() {
		return sName;
	}
	public void setsName(String sName) {
		this.sName = sName;
	}
	public int getiAge() {
		return iAge;
	}
	public void setiAge(int iAge) {
		this.iAge = iAge;
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值