Java中的HashSet集合知识总结:底层原理和实现操作

本文详细介绍了JavaHashSet的数据结构(哈希表),包括底层原理、创建过程、添加元素策略(利用哈希值计算索引并处理冲突)、查询性能优化以及如何保证元素唯一性。同时涵盖了自定义类对象的存储和文本表示方法。
摘要由CSDN通过智能技术生成

HashSet基本介绍

HashSet集合底层采取哈希表存储数据,对于增删改查的操作数据性能都较好。

JDK8版本后的的哈希表是数组、链表、红黑树的结合体。


底层原理

创建集合

创建HashSet集合,内部会存在一个长度为16个大小的数组。

public HashSet() {
    map = new HashMap<>();
    //使用默认初始容量16和默认加载因子0.75构造一个空HashMap
}

添加元素

调用集合的添加方法,通过对象的hashCode方法计算出哈希值,进而得到应存入的索引位置(哈希值 mod 数组长度)。

//HashMap中的hash方法源码
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    //对哈希值扰动,进行二次哈希操作,可以一定程度地减少链表挂载的数量
}
//HashMap中的putVal方法源码摘录
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)ll)
    //此计算方法与(哈希值 mod 数组长度)值相同,但运算效率更高
    ……
}

应存入索引位置计算方法:

  1. 哈希值的计算:为了打散哈希表的元素,一定程度地减少链表挂载的数量,在具体源码中是先使用对象的hashCode方法得到原始哈希值,再将原址哈希值向右移动16位进行一次哈希扰动,最后拿扰动后的哈希值与原始哈希值做异或操作即二次哈希操作,获得哈希值。
  2. 为了提高运算效率,不直接将哈希值模于数组长度,而是将对数组长度减一后的值和计算出的哈希值进行与运算,得到应存入的索引位置。

判断计算出来的索引位置是否为null,如果是null则直接存入;如果不是null表示该位置有元素,则调用equals方法比较属性值,如果相同则不存,如果不同则存储数组。

提高查询性能

存入元素后,为提高查询性能,判断哈希表结构是否需要进行调整,如果重新调整则需重新计算应存入的索引位置。

(1)当数组中的元素个数超过了 ( 数组长度 * 加载因子0.75 ) 时,扩容原数组2倍的大小。(注意是元素个数而不是有元素的索引位置)

(2)当链表挂载的元素超过阈值8个时,如果数组长度没有超过64则扩容原数组2倍的大小,如果数组长度超过了64则将链表转为红黑树。


基本操作

集合元素的遍历

采取集合元素的通用遍历方式,即迭代器、增强for循环、forEach三种方法(参考 Java集合的遍历方式:Iterator和ListIterator - 集合元素通用遍历方式

常见API

public int size()                 //返回存储的元素个数
public boolean isEmpty()          //返回集合是否为空
public boolean contains(Object o) //返回集合是否包含o元素
public boolean add(E e)           //添加元素e
public boolean remove(Object o)   //删除元素o
public void clear()               //清空集合中的元素

存储自定义类对象

public class City {
    private String name;
    private char carID;
    private int adminID;
    public City(String name, char carID, int adminID){
        this.name=name;
        this.carID=carID;
        this.adminID=adminID;
    }
}

保证元素唯一性

为了保证元素的唯一性,需要同时重写自定义类中的hashCode方法和equals方法。

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        City minCity = (City) o;
        return carID == minCity.carID && adminID == minCity.adminID && Objects.equals(name, minCity.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, carID, adminID);
    }

以文本方式表示对象

为了在输出HashSet集合时显示对象属性值,需要重写自定类中的toString方法。

    @Override
    public String toString() {
        return "City{" +
                "name='" + name + '\'' +
                ", carID=" + carID +
                ", adminID=" + adminID +
                '}';
    }

参考资料

黑马程序员-2023新版Java视频教程-进阶篇Day11-01-HashSet集合

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

STRANGEX-03

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值