Bootstrap

HashSet的特点及实现原理

Set集合概述

  • Set集合的特点是无序,无下标,不重复
  • Set集合的所有方法都来自于其父接口Collection接口,换言之,其不存在特有的方法

HashSet(重中之重)

  • 基于HashCode实现集合不重复
  • 哈希值相同时会调用equals方法判断,若为true则不插入
package com.design;

import java.util.HashSet;

public class HashSetDemo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("小米");
        set.add("苹果");
        set.add("华为");
        set.add("华为");
        System.out.println(set);
    }
}

在这里插入图片描述
我们可以看到,虽然我们调用了HashSet对象的add方法添加华为了两次。但程序显然并没有报错,不过最终输出Set时发现华为并没有插入。我们插入的顺序是1小米2苹果3华为,可输出的顺序是1苹果2华为3小米。
以上现象便验证了HashSet的无序性以及不可重复性

HashSet遍历

由于HashSet继承子Collection接口,所以其自然拥有Collection的共有遍历方法,但由于其无序性(不能取得元素下标),则不能使用for循环遍历

  • foreach遍历
  • iterator遍历
package com.design;

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

public class HashSetDemo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        set.add("小米");
        set.add("苹果");
        set.add("华为");
        set.add("华为");
        System.out.println("1.增强for");
        for (String str :
                set) {
            System.out.println(str);
        }
        System.out.println("2.迭代器");
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext())
        {
            System.out.println(iterator.next());
        }
    }
}

HashSet的存储结构是基于哈希表。

什么是哈希表?
通俗的来讲就是数组+链表,在JDK1.8之后引入了红黑树来存储大量数据。
在Object类下有一个方法叫做hashCode(),我们可以用此方法获取哈希值
Java很多内置对象都对HashCode进行了重写

  public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

以上是Java对字符串的hashCode方法的重写源码,我们在HashCode方法中经常会看到31这个数字
之所以使用数字31是因为
1.31是质数,可以减少散列冲突2.31可以提高执行效率

基于字符串的hashCode()源码分析

变量hash为缓存中为字符串准备的hash值 默认为0。
将hash赋给变量h
当h等于0或者当前字符串的长度大于0时
我们将字符串转换为字符数组
然后对当前的h值乘以31再加上字符数组在索引为i的字符的编码值。
如此循环字符串的长度次。最终返回哈希值。
这便是字符串的hashCode方法,类似的方法还有取余等方式,不同的算法会有不同的特点。但相同的是
都会从或是地址或是长度等因素综合计算出一个不容易重复的数值来区分不同的元素。

如何通过哈希值进行集合的存储呢?

通过hashCode方法我们获取到了元素的哈希值,再通过哈希值找到数组的相应下标,进行存储。
如果一个HashCode的方法是对元素进行%10取得哈希值,那么整形33调用hasCode方法之后获得的哈希值便是3,我们只需要将该元素存储到下标为3的内存空间即可
在这里插入图片描述

细心的小伙伴可能已经发现了,如果有两个元素调用hashCode返回的哈希值一模一样呢?就比如在该例中,我们已经存储了33在3号内存空间,可如果来了43号元素,岂不是无处可放了?
事实上,不论是哪一种HashCode方法都有可能出现不同元素返回相同元素的情况,这是不可避免的!
此时 我们需要在33号元素后以链表的形式添加43号元素
在这里插入图片描述
以上便是以数组加链表的方式来实现的哈希表。
通过这种方式我们可以大大加快判断是否重复的速度,从而提升程序的性能

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;