Java集合之aHshset

对于今晚上讲课的总结:

public class HashSet_ {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        set.add("lucy");//会返回一个boolean值
        set.add("lucy");//加入不了
        set.add(new Dog("tom"));//true
        set.add(new Dog("tom"));//true
        System.out.println("set=" + set);
        //非常经典的面试题
        set.add(new String("111"));//true
        set.add(new String("111"));//false
        System.out.println("set=" + set);
    }
}
class Dog { //定义了 Dog 类
    private String name;
    public Dog(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }
}

 输出:

true
true
set=[Dog{name='tom'}, Dog{name='tom'}, lucy]
true
false
set=[111, Dog{name='tom'}, Dog{name='tom'}, lucy]

对于为什么第二个lucy不能添加,第二个tom可以添加成功以及为什么第二个111不能添加成功的解释:

首先我们要明白hsahset添加元素的原理:HashSet是Java中的一种集合类,它基于哈希表实现。在HashSet中,每个元素都会被存储在一个桶(bucket)中,桶是根据元素的哈希值来确定的。因此,如果两个元素的哈希值相同,它们就会放在同一个桶里,称为哈希冲突。

在HashSet中,元素的存储顺序不是按照插入的顺序来存储的,而是按照元素的哈希值来存储的。当需要查找某个元素时,HashSet会先计算这个元素的哈希值,然后根据哈希值找到对应的桶,最后再在桶中进行查找操作。

当我们向HashSet中添加一个新元素时,HashSet会首先计算该元素的哈希值,并根据哈希值确定该元素应该放置的桶。如果桶为空,就直接将该元素添加到桶中;否则就需要遍历桶中已有的元素,判断是否存在相同的元素。如果已存在相同元素,则不会添加,否则就将其添加到桶中。

HashSet的内部采用了数组加链表或红黑树的方式来管理桶。当桶中元素较少时,采用数组加链表的方式;当元素数量增多时,会自动转换为红黑树的方式,以提高查找效率。

当添加元素到 HashSet 中时,首先会根据元素的 hashCode() 值找到它在哈希表中对应的位置,然后调用该元素的 equals() 方法,判断该位置上是否有相同的元素。如果有相同的元素,则不添加新元素,返回 false;否则添加新元素,返回 true。

  • 对于第二个 "lucy" 字符串和第一个 "lucy" 字符串来说,它们的 hashCode() 值是相同的,因为它们的内容相同。当添加第二个 "lucy" 字符串时,HashSet 会先根据该字符串的 hashCode() 找到它在哈希表中的位置,然后调用第一个 "lucy" 字符串的 equals() 方法进行比较。由于两个字符串的内容相同,equals() 方法返回 true,因此 HashSet 认为第二个 "lucy" 字符串已经存在于 HashSet 中,不再添加新元素,直接返回 false。
  • 对于第二个 Dog 对象和第一个 Dog 对象来说,虽然它们是两个不同的对象,但它们的 name 属性相同。在默认情况下,Object 类中的 hashCode() 方法会根据对象的内存地址计算其哈希值,而 equals() 方法则只比较两个对象的内存地址是否相同。因此,两个不同的 Dog 对象的 hashCode() 和 equals() 方法的返回值都不相同,HashSet 认为它们是不同的元素。但是,我们可以在 Dog 类中重写 hashCode() 和 equals() 方法,让它们根据对象的属性计算其哈希值和相等性,这样就可以根据属性判断两个不同的 Dog 对象是否相同了。
  • 这两个 "111" 字符串具有相同的内容,但是在示例代码中它们是两个不同的对象。由于 String 类已经重写了 hashCode() 方法和 equals() 方法,它们会根据字符串的内容计算哈希值和相等性判断结果。
  • 因此,当将第一个 "111" 字符串添加到 HashSet 中时,HashSet 会先调用它的 hashCode() 方法来计算哈希值,然后根据哈希值找到它在哈希表中的位置,最后再调用它的 equals() 方法,判断该位置上是否已经有相同元素。由于此时哈希表为空,HashSet 会将第一个 "111" 字符串添加到哈希表中,并返回 true。
  • 当将第二个 "111" 字符串添加到 HashSet 中时,HashSet 同样会先调用它的 hashCode() 方法来计算哈希值,然后根据哈希值找到它在哈希表中的位置,最后再调用它的 equals() 方法,判断该位置上是否已经有相同元素。由于这两个字符串的 hashCode() 和 equals() 方法返回值都相同,HashSet 认为这两个字符串是相同的元素,不能将第二个 "111" 字符串添加到 HashSet 中,而是直接返回 false。
  • 请注意,String 类的 hashCode() 方法和 equals() 方法都是根据字符串的内容来计算的,因此具有相同内容的字符串在 HashSet 中被认为是相同的元素,而与它们是不是同一个对象没有关系。

这里可能有人会对第二个例子和第三个例子有疑惑。

因为Object 类中的 hashCode() 方法和 equals() 方法的计算方式与 String 类不同。在默认情况下,Object 类中的 hashCode() 方法会返回当前对象的内存地址经过处理后的哈希值,而 equals() 方法只比较两个对象的内存地址是否相同。因此,对于不同的对象,它们的 hashCode() 和 equals() 方法的返回值通常是不同的。由于 Object 类中的 hashCode() 方法和 equals() 方法无法根据对象的属性来计算哈希值和相等性判断结果,因此如果需要使用 HashSet 这样的集合类型来存储自定义的对象,我们通常需要在自定义类中重写这两个方法,让它们根据对象的属性进行计算。而对于 String 类来说,它已经重写了 hashCode() 方法和 equals() 方法,让它们根据字符串的内容来计算哈希值和相等性判断结果。因此,无论字符串是不是同一个对象,只要它们的内容相同,它们的 hashCode() 和 equals() 方法的返回值都是相同的。关于string类的hashCode() 方法和 equals() 方法的计算在 String 类中,hashCode() 方法被重写为根据字符串的内容计算哈希值。具体地说,它会将字符串看作一个字符序列,对每个字符计算一个权重值,然后将这些权重值相加,最后将结果转换成 int 类型作为哈希值返回。这种计算方式可以保证相同内容的字符串具有相同的 hashCode() 返回值。

在 String 类中,equals() 方法也被重写为根据字符串的内容进行相等性判断。具体地说,它会先比较两个字符串的长度是否相等,如果不相等,则认为两个字符串不相等;如果长度相等,则逐个比较两个字符串中的字符是否相等。如果所有的字符都相等,则认为两个字符串相等,否则认为它们不相等。由于 String 类的 hashCode() 方法和 equals() 方法都是根据字符串的内容计算的,因此相同内容的字符串在 HashSet 中被认为是相同的元素,而与它们是不是同一个对象没有关系。

对重写的解释:

要使第二个名为 "tom" 的 Dog 对象无法添加到 HashSet 中,需要在 Dog 类中重写 hashCode() 和 equals() 方法,让它们根据 Dog 对象的 name 属性进行计算。

重写后 :

class Dog {
    private String name;
    public Dog(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }
    @Override
    public int hashCode() {
        // 根据 name 属性计算哈希值
        return Objects.hash(name);
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Dog)) return false;
        Dog other = (Dog) obj;
        // 比较 name 属性是否相等
        return Objects.equals(name, other.name);
    }
}

重写后输出

true
false
set=[Dog{name='tom'}, lucy]
true
false
set=[Dog{name='tom'}, 111, lucy]

在上述代码中,我们重写了 Dog 类的 hashCode() 和 equals() 方法。在 hashCode() 方法中,我们使用 Objects 类的 hash() 方法根据 name 属性计算哈希值;在 equals() 方法中,我们首先判断 obj 是否是当前对象本身,如果是则直接返回 true;然后判断 obj 是否是一个 Dog 对象,如果不是则返回 false;最后将 obj 强制转换为 Dog 对象,并比较两个对象的 name 属性是否相等,如果相等则返回 true,否则返回 false。通过这样的 hashCode() 和 equals() 方法,可以让 HashSet 根据 Dog 对象的 name 属性进行哈希值计算和相等性判断,从而确保同名的 Dog 对象不会被重复添加到 HashSet 中。

因为在默认情况下,HashSet 会使用对象的 hashCode() 方法和 equals() 方法来进行元素的哈希值计算和相等性判断。而在示例代码中,两个名为 "tom" 的狗虽然名字相同,但是它们是两个不同的对象,并且没有重写 hashCode() 和 equals() 方法。因此,HashSet 只会根据这两个对象的内存地址来判断它们是否相等,尽管它们的名字相同,但是它们分别分配了不同的内存地址,因此它们被认为是两个不同的元素。如果没有重写 hashCode() 和 equals() 方法,无法根据对象的属性来进行元素的哈希值计算和相等性判断,从而会导致同名的狗被重复添加到 HashSet 中。

String s1= new String("111");
String s2 = new String("111");
System.out.println(s1==s2);//输出false
String s3 = new String("111");
String s4 = new String("111");
System.out.println(s1.equals(s2)); // 输出 true

Java 中,== 和 equals() 都是用来比较两个对象的方法,但它们的比较方式不同。== 操作符比较的是两个对象的引用地址是否相同,也就是说它们是否指向同一块内存地址。使用 == 进行比较时,如果比较的两个对象引用地址相同,则返回 true,反之则返回 false。equals() 方法比较的是两个对象的内容是否相同,也就是说它们所包含的数据是否相等。默认情况下,equals() 方法比较的是两个对象的引用地址是否相同,和 == 操作符的作用相同。但是,对于一些类(如 String 类),它们重写了 equals() 方法以实现按值比较。一般来说,如果我们要比较两个对象的值是否相等,应该使用 equals() 方法进行比较。如果我们要比较两个对象的引用地址是否相等,可以使用 == 操作符进行比较。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值