您的位置:首页 > 其它

集合框架 HashSet集合

2013-05-26 09:36 120 查看

集合框架——HashSet

在介绍HashSet集合之前我们先了解下 equals == 和 toString

1,浅谈equals与 == 的区别

Java中测试两个变量相等有两种方式 “==” 和 equals方法。
对于基本类型变量 == 比较的是字面值(必须都是数值型变量)
对于引用类型 == 比较的是 引用的值(一个地址)

对于自己创建的类,继承的是Object 类的equals方法,如果该类不覆盖
equals方法,那么调用时,它还是会去比较对象引用的值(即指向的地址)

对于jar包中的类,哪些类覆盖了该方法,哪些没有,到具体的时候可以去查看
该类的API文档,我们只需要知道equasl 与 == 的区别即可。

现在有下列需求:
定义两个学生对象
1,包含姓名 年龄 学号
2,不指定比较方式,比较两个学生对象是否是同一对象。
代码如下:

class Student
{
private String name;
private int age;
private String idStr;
public Student (String name,int age,String idStr){
this.name = name;
this.age = age;
this.idStr = idStr;
}
public String getName(){
return this.name;
}
public int getAge(){
return this.age;
}
public String getIdStr(){
return this.idStr;
}
}
public class MyEquals
{
public static void main(String args[]){
Student stu1 = new Student("李磊",24,"0912");
Student stu2 = new Student("韩梅梅",22,"0934");
System.out.println(stu1 == stu2);
System.out.println(stu1.equals(stu2));
}
}


2,HashSet集合

HashSet集合底层数据结构是哈希表。

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

是同过调用存入集合中元素自身的两个方法:hashCode 和 equals 方法

在这之前,我们先明确两个问题:

2.1 hashCode值 和 对象的地址 二者的联系和区别

对于自定义类

当新建一个自定义类,它会继承hashCode方法。当我们调用hashCode方法时,会返回一个int数据?这个数据是怎么得来的了?它与很多内容
相关,与这个对象的内存地址,对象上的成员变量等都有关,这些都参与了哈希值的运算。所以可以明确的是:哈希值不是对象的地址。怎
样判断引用是否相同?其实是比较引用指向的地址是否相同。
如果简单的认为hashCode返回的值是地址值,那么覆盖此方法,使其返回值都相同,那么所有新建的对象不是都相同吗?显然不是。

2.2equals方法到底在比什么

很多地方都提到了 equals方法与 == 的区别 , == 这个操作符是java固有的操作符,它的比较方式是固定的:基本类型数值比较,

引用类型比较。都有自己的规定和计算方法。 但是对于equals方法,它是一个方法,所有类继承自Object类。由于重载的原因,使其

成为一个很灵活的比较方法,你可以按自己的想法去指定两个对象如何相等。

这时需要关心的是,查看谁的equals方法:谁调用查看谁,看它指定的比较方式。

看下面一段代码:

public class MyHashSet
{
public static void main(String args[]){
E element = new E();
E element2 = new E();
String str = new String("这不合逻辑!");

sop(element.hashCode());
sop(element2.hashCode());
sop(str.hashCode());//String 覆盖了hashCode方法,有自己计算哈希值的方法
sop(element == element2);
sop(element.equals(element));
//sop(element == str);这句在编译时就无法通过。
sop(element.equals(str));
}
public static void sop(Object obj){
System.out.println(obj);
}
}
class E
{
public boolean equals(Object obj){
//你可以很不负责的让两个对象“相等”
return true;
}
public int hashCode(){
return 60;
}
}


---------- 运行java程序 ----------

60

60

-342913705

false

true

True

从运行结果来看:== 检查的是两个对象的地址值是否相同,对于不同类型在编译时就会出错。

而指定的equals方法,理论上可以使任何对象“相等”,不过这种相等并无实际意义。

2.3 HashSet集合的存储方式

有了以上内容做铺垫,我们在来看看HashSet是如何保证元素的唯一性,就比较清楚了。

你可以把HashSet集合看成是一个装东西的容器:玻璃瓶,罐子都行。只是在装入东西时,要按这个容器的规则进行。

当我们新建一个对象,以自定义对象为例。因为有hashCode方法,这个对象总会产生一个哈希值,不管你的自定义类覆盖hashCode方法与否。

当我们向HashSet容器放元素(或者对象,其实是对象的引用)时,元素放在哪是一个问题。HashSet这个集合的特点是,每个元素都有哈希值,依据元素的哈希值,决定存放位置。

在进行比较时,先调用的是hashCode方法。

当向HashSet集合中存储元素时,新添加的元素会与已存入的元素做一一比较:

如果比较的两个元素通过自身hashCode方法算出的哈希值相同,进一步调用 自身的equals方法,如果equals方法返回值为真,那么这两个元素被判定为 同一元素,如果equals方法返回假,两个元素判定为不同。只是在集合中存 储位置上有一个关联。

如果比较的两个元素通过自身的hashCode方法算出的哈希值不同,那么不调 用equals方法,也就判定这两个元素不同。

所以可以进行实验:

自定义一个类,不覆盖hashCode方法和equals 方法。那么它会沿用Object类中计算哈希值的方法和equals方法。由于hashCode方法的实现很复杂(目前仍未看懂),但是其计算方式可以最大程度防止出现相同哈希值相同。

至于equals方法就比较简单,我们可以看看:

public boolean equals (Object obj){

return this == obj;

}也就是检查其实否为同一对象。

看代码如何实现:

import java.util.HashSet;
public class MyHashSet2
{
public static void main(String args[]) throws Exception{
HashSet con = new HashSet();
con.add(new Student(21,"Niki"));
con.add(new Student(24,"Caesar"));
con.add(new Student(22,"HanMeiMei"));
con.add(new Student(24,"Caesar"));
sop(con.size());
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
}
class Student
{
private int age;
private String name;
public Student(int age,String name){
super();
this.age = age;
this.name = name;
}
}


---------- 运行java程序 ----------

4

通过结果可以看出,四个Student对象都存入到集合中了。

现在的需求变了,当学生的姓名和年龄相同时,就判定为相同对象(肯定不能用 == ,它是在比较地址值,当新建对象时,就会为其开辟存储空间,这种物理空间肯定不相同)。如何改写hashCode方法了??原则上hashCode的计算公式中要包含作为比较标准的那些参数:此处是年龄和姓名。

看看代码是如何实现的:

import java.util.HashSet;
public class MyHashSet2
{
public static void main(String args[]) throws Exception{
HashSet con = new HashSet();
con.add(new Student(21,"Niki"));
con.add(new Student(24,"Caesar"));
con.add(new Student(22,"HanMeiMei"));
con.add(new Student(24,"Caesar"));
sop(con.size());
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
}
class Student
{
private int age;
private String name;
public Student(int age,String name){
super();
this.age = age;
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public boolean equals(Object obj){ //指定比较方式
if(!(obj instanceof Student))
return false;
Student st = (Student)obj;
return this.name.equals(st.name) && this.age == st.age;
}
public int hashCode(){ //用年龄和姓名做参数计算哈希值
return (name.hashCode()+age*39);
}
}


---------- 运行java程序 ----------

3

输出完成 (耗时 0 秒) - 正常终止

看到输出结果是3,证明按照指定的方式实现了元素的存储。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: