在HashSet中添加mutuable类型变量并使用contains函数

我们创建一个ArrayList类charAray ,向其中添加元素,之后将charAray添加到HashSet setArray中,用contains进行测试,之后改变charAray中的元素再用contains进行测试,最后用equals方法进行测试。

public static void main(String[] args) {
        ArrayList<Character> charAray = new ArrayList<>();
        charAray.add('a');
        Set<ArrayList> setArray = new HashSet<>();
        setArray.add(charAray);
        System.out.println(setArray.contains(charAray));
        charAray.add('b');
        System.out.println(setArray.contains(charAray));
        for (ArrayList temp : setArray) {
            System.out.println(temp.equals(charAray));
        }
    }

得到的结果如下

true
false
true

可以看到第二个输出为false但是第三个为true,这设计到了HashSet和ArrayList的内部实现。正如其名,HashSet内部是一个哈希表的结构,在添加元素时,用C语言对比,可以将HashSet看做一个数组,其内部存放的是指向ArrayList的指针,ArrayList类的内部方法hashCode会根据自身的元素返回一个int类型变量,HashSet根据hashCode的返回值决定将元素插入到哪个位置。
ArrayList中的hashCode定义如下

   public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }

很明显,ArrayList中添加元素的时候将导致其hashCode的返回值改变,但是HashSet在ArrayList中的hashCode发生变改变时,不会修改元素的位置。当调用contains函数测试charAray时,HashSet会根据对象的hashCode值定位到其内部元素obj,再使用equals方法比较charAray和obj是否相同,并返回true或false。由于charAray的hashCode已经发生变化,HashSet无法定位到我们想要的对象,所以调用contains函数将会返回false。我们无法修改ArrayList的实现,所以这个问题无法解决,但是对于我们的自定义类我们的自由度很高,可以探索一下解决办法,能不能在修改对象属性或者对象等价的情况下均返回true。
定义类Apple,不对hashCode和equals重写

public class Apple {
    private String color;

    public Apple(String color) {
        this.color = color;
    }
	

    public void changeColor(String color) {
        this.color = color;
    }

    public static void main(String[] args) {

        Set<Apple> S = new HashSet<>();
        Apple apple1 = new Apple("green");
        S.add(apple1);
        System.out.println(S.contains(apple1));
        System.out.println(apple1.hashCode());

        apple1.changeColor("123");
        System.out.println(S.contains(apple1));
        System.out.println(apple1.hashCode());
}

运行如下代码

public static void main(String[] args) {

        Set<Apple> S = new HashSet<>();
        Apple apple1 = new Apple("green");
        S.add(apple1);
        System.out.println(S.contains(apple1));
        System.out.println(apple1.hashCode());
        System.out.println();

        Apple apple2 = new Apple("green");
        System.out.println(S.contains(apple2));
        System.out.println(apple2.hashCode());
        System.out.println();

        apple1.changeColor("red");
        System.out.println(S.contains(apple1));
        System.out.println(apple1.hashCode());
    }

得到如下结果

true
488970385

false
1209271652

true
488970385

在不重写hashCode方法时,我们看到修改类中元素不会改变hashCode值,此外Object中的equals是直接对地址进行比较,所以第三个测试返回true。但是这种情况下即使两个对象是等价的,也无法用contains进行判断。我们修改hashCode和equals方法,如下所示

    @Override
    public int hashCode() {
        return color.length();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Apple))
            return false;
        if (obj == this)
            return true;
        return this.color.equals(((Apple) obj).color);
    }

此时重新运行代码,得到如下结果

true
5

true
5

false
3

这时第二种测试得到了true但是第三种得到false,为了让这两种测试都得到true,我们可以修改类内部定义:添加final int变量HC(代表hashCode),并修改构造方法和hashCode方法

	final private int HC;

    public Apple(String color) {
        this.color = color;
        HC = color.length();
    }
    
    @Override
    public int hashCode() {
        return HC;
    }

这样在实现一个类的时候其hashCode便被定下且不会再发生改变,运行代码我们可以得到如下结果

true
5

true
5

true
5

这样看似很好但是运行如下代码会遇到新的问题

    public static void main(String[] args) {
        Set<Apple> S = new HashSet<>();
        Apple apple1 = new Apple("green");
        S.add(apple1);

        apple1.changeColor("123");
        Apple apple3 = new Apple("123");
        System.out.println(S.contains(apple3));

    }

输出结果为false,和我们的想法不符合,由于HashSet使用了哈希表的情况,这两种情况不能同时满足。所以在定义自己的可变数据类型时,最好不要修改hashCode方法,可能会导致各种想不到的错误,此外在HashSet中加入可变数据类型的时候一定要小心使用。而使用不可变数据类型时,因为内部数据不会发生改变,所以我们可以大胆操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 JavaScript ,没有名为 "hashset" 的内置变量类型。JavaScript 提供了一些内置的基本数据类型,包括数字(Number)、字符串(String)、布尔值(Boolean)、对象(Object)、数组(Array)、函数(Function)等。 如果你需要实现哈希集合(hashset)的功能,可以使用对象(Object)或者 ES6 引入的 Set 数据结构来实现。 1. 使用对象(Object):你可以使用对象来实现类似于哈希集合的功能,将元素存储为对象的属性,利用对象的键值对特性来实现快速的查找和去重。 ```javascript const hashSet = {}; // 添加元素 hashSet['element1'] = true; hashSet['element2'] = true; // 检查元素是否存在 if (hashSet['element1']) { console.log('element1 存在'); } // 删除元素 delete hashSet['element2']; ``` 2. 使用 Set 数据结构:ES6 引入了 Set 数据结构,它是一种有序且不重复的集合,可以用来存储唯一的值。Set 提供了一些常用的方法,如添加元素、删除元素、判断元素是否存在等。 ```javascript const hashSet = new Set(); // 添加元素 hashSet.add('element1'); hashSet.add('element2'); // 检查元素是否存在 if (hashSet.has('element1')) { console.log('element1 存在'); } // 删除元素 hashSet.delete('element2'); ``` 使用对象或 Set 来实现哈希集合的选择取决于你的具体需求。如果你需要更多的集合操作方法和性能优化,推荐使用 Set 数据结构。 希望这个解答能够帮助到你。如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值