黑马程序员-ArrayList、HashSet比较和HashCode分析

时间:2022-07-27 16:47:22

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

首先以ArrayList为例:

import java.util.*;
class ReflectPoint {
    public int x;
    public int y;
    public  ReflectPoint(int x,int y){
        this.x=x;
        this.y=y;
    }
}
public class HashAndArray {
    public static void main(String[] args) {
        ReflectPoint rp1=new ReflectPoint(6,6); //构造三个ReflectPoint对象
        ReflectPoint rp2=new ReflectPoint(7,7);
        ReflectPoint rp3=new ReflectPoint(6,6);
        Collection coll=new ArrayList();        //实例化Collection
        coll.add(rp1);                          //加入元素
        coll.add(rp2);
        coll.add(rp3);
        coll.add(rp1);
        System.out.println(coll.size());        //得出容器长度
    }
}

ArrayList是将元素有序的放进容器里,而且可以重复,所以输出长度4.


下面将ArrayList改为HashSet:

import java.util.*;
class ReflectPoint {
    public int x;
    public int y;
    public  ReflectPoint(int x,int y){
        this.x=x;
        this.y=y;
    }
}
public class HashAndArray {
    public static void main(String[] args) {
        ReflectPoint rp1=new ReflectPoint(6,6); //构造三个ReflectPoint对象
        ReflectPoint rp2=new ReflectPoint(7,7);
        ReflectPoint rp3=new ReflectPoint(6,6);
        Collection coll=new HashSet();        //改为HashSet
        coll.add(rp1);                          //加入元素
        coll.add(rp2);
        coll.add(rp3);
        coll.add(rp1);
        System.out.println(coll.size());        //得出容器长度
    }
}
输出长度为3。HashSet将元素放进容器之前会先检查容器中是否有相同的元素,如果有则不放进去,如果非要放进去则将原先的元素删掉(注意不是覆盖),然后再放进去,是无序的放进去。但是程序中rp1和rp3对象都是(6,6),理应相等输出2,但结果为3,这是因为默认的equals()比较的是hashCode值。

如果想得到输出结果为2,则应覆写Object的equals方法和hashCode。下面来覆写equals()方法和hashCode()方法。(其余代码略)

    @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;
    }
覆写equals()方法和hashCode()方法得到的结果为2.但如果只覆写equals()方法,而不覆写hashCode()方法会怎样呢?(代码略,可自行测试)

通过反复运行,得出的结果有两种可能性,2或者3。这说明运行结果与hashCode()有关,下面讲解hashCode()的具体作用。

hashSeth存放对象元素是根据哈希值来的,根据哈希值的不同放在不同的内存里。当要将对象存进hashSet首先对对象进行哈希运算,经过哈希运算将对象放进不同区域,因为hashSet是不允许有重复内容的,所以要存进对象时先和那片区域的对象进行比较,看是否相等(这时已根据哈希值找到要存储的区域),如果有相等的对象则不存进去。如果没有重复的元素,则放进去。所以当equals()相等,哈希值也应该相等。但是当元素不放进哈希集合时,不用覆写hashCode()方法。
但要注意,在已经覆写hashCode()的程序中,不要去修改参与哈希运算的字段,例如本程序的y值,下面来做个实验,如果改变y值会发生什么事情。

import java.util.*;
class ReflectPoint {
    public int x;
    @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;
    }
    public int y;
    public  ReflectPoint(int x,int y){
        this.x=x;
        this.y=y;
    }
}
public class HashAndArray {
    public static void main(String[] args) {
        ReflectPoint rp1=new ReflectPoint(6,6); //构造三个ReflectPoint对象
        ReflectPoint rp2=new ReflectPoint(7,7);
        ReflectPoint rp3=new ReflectPoint(6,6);
        Collection coll=new HashSet();        //改为HashSet
        coll.add(rp1);                          //加入元素
        coll.add(rp2);
        coll.add(rp3);
        coll.add(rp1);
        rp1.y=8;                               //将rp1对象的y值从7改为8
       c                       //从coll容器中删除rp1元素
        System.out.println(coll.size());        //得出容器长度
    }
}
注意到coll容器进行了删除rp1元素的操作,所以结果应为只剩一个元素,输出1,但是结果却还是2。这是因为在执行
coll.remove(rp1);
代码之前更改了rp1对象的y值,而y值恰恰参与了哈希值的运算,所以没能将rp1元素删掉。那为什么更改参与哈希运算的y值就不能删掉rp1?

如之前所说,HashSet中元素的存储位置是根据哈希运算后的值决定的。当执行

 coll.add(rp1);
时rp1的y=7,将这时的哈希值我们先假定为20,那么编号为20。根据编码rp1对象被安排进了内存的某块区域常住。当改变rp1的y为8时,编号也会跟着改变。容器删除元素是根据哈希值判定在某一区域进行逐一检查是否与要删除的内容匹配,找到匹配的删掉。
 coll.remove(rp1);
执行上述语句是,java虚拟据只能说有可能找到rp1,如果修改后的rp1还在修改之前的区域的话。所以执行结果并不能百分百确定,可能是1,可能是2。