java——Hashset中add()方法的执行过程

本文深入解析了HashSet中add()方法的执行流程,包括如何避免重复数据的机制,以及内部hash和resize方法的作用。通过实例代码,展示了当两次添加相同元素时,HashSet是如何保持其唯一性的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HashSet中的add()方法的执行过程。
HashSet sets = new HashSet();
set.add()不能添加重复数据,具体原因如下:
例如:

public class Test {
	public static void main(String[] args) {
		HashSet<Object> sets = new HashSet<Object>();
		sets.add(“tom”);
		sets.add(“tom”);
		System.out.println(sets.size());
	}
}

输出的set长度将是1。

HashSet<Object> sets = new HashSet<Object>();

首先在sets.add(“tom”);sets.add(“tom”);两行打断点。
Debug运行,按F5进入add()内,
在这里插入图片描述
此处map指向HashSet()
再点F5进入该方法在这里插入图片描述
此处hash()将在本类下面用到在这里插入图片描述
此处在627行打断点按F6运行至此,详细代码如下:

627       if ((tab = table) == null || (n = tab.length) == 0)
628           n = (tab = resize()).length;
629    if ((p = tab[i = (n - 1) & hash]) == null)
630     tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
633      if (p.hash == hash &&
634          ((k = p.key) == key || (key != null && key.equals(k))))
635          e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
646                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
652            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
661        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    

第一次传入tom时
627行,table是全局变量,传入值为null
628行,n的值为16(等式右边的默认值)
629行,此处hash将调用如下代码,此处tab[i = (n - 1) & hash]将为null(第一次传入时为空)代码向下运行,
630行, 为tab[i]赋值
代码运行至661行及以下,结束!

 static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

第二次传入tom时
此时table在上一次628行执行后将不为null,627行不会进入,因(n = tab.length) == 0),此时tab[i = (n - 1) & hash]中i = (n - 1) & hash与上次结果一样,但tab已不为空;将直接进入633行,
此处,前后两次获取的hash相等(String中同一变量调用的hash返回值相同),e=p ,代码进入652行改写value后结束(不用管这个),第二次的tom未存进去。所以set里面只有一个tom
以上即为HashSet中add的执行过程。

### Java 中 `HashSet` 的使用教程 #### 什么是 `HashSet` `HashSet` 是基于哈希表的 `Set` 实现,不允许存储重复元素。该数据结构不保证元素的顺序,并且大部分方法依赖于底层的 `HashMap` 来完成操作[^1]。 #### 初始化方式 存在多种构造器用于创建 `HashSet` 对象: - **无参数构造器**:这是最简单的初始化形式,它会默认设置初始容量和加载因子。 ```java Set<Integer> set = new HashSet<>(); ``` - **带有指定集合作为参数的构造器**:可以将已有的集合转换成 `HashSet`。 ```java Collection<String> collection = Arrays.asList("apple", "banana"); Set<String> fruitSet = new HashSet<>(collection); ``` - **带自定义负载因子或容量大小的构造器**:允许开发者更精细地控制内部数组的增长行为。 ```java // 定义初始容量为20, 负载因子为0.75f Set<Double> numbers = new HashSet<>(20, 0.75f); ``` #### 添加与移除元素 向 `HashSet` 插入新成员非常简单,只需调用 `add()` 方法即可;而要删除某个特定项,则可利用 `remove()` 函数。 ```java // 向集合中添加三个整数 set.add(1); set.add(2); set.add(3); // 移除其中一个数 if (set.contains(2)) { set.remove(2); } ``` #### 判断唯一性和处理冲突 当尝试加入新的对象时,`HashSet` 将依据此对象的 `hashCode()` 返回决定其位置索引。为了确保不同实例不会被误认为相同,还需要覆写 `equals(Object obj)` 方法来进行最终比较。如果两个对象具有相同的散列码却并不相等(即它们代表不同的实体),那么这些条目会被放置在同一桶位上形成链表或者红黑树以解决潜在的碰撞问题[^3]。 #### 时间复杂度分析 通常情况下,由于采用了优秀的哈希算法设计,使得增删查改的操作效率接近常量时间 O(1),但在某些特殊条件下——比如大量键产生了严重的哈希冲突——性能可能退化至线性级别 O(n)[^4]。 #### 示例代码展示 下面给出一段完整的例子演示如何运用上述知识点构建并操控一个基本类型的 `HashSet`: ```java import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) { // 创建一个新的HashSet实例 Set<Integer> myNumbers = new HashSet<>(); // 增加几个数字到这个集中 myNumbers.add(10); myNumbers.add(20); myNumbers.add(30); System.out.println("当前集合中的元素:" + myNumbers); // 测试是否包含某具体 boolean hasNumber = myNumbers.contains(20); System.out.println("是否存在20? " + hasNumber); // 清理掉之前存在的那个数 if(hasNumber){ myNumbers.remove(20); } System.out.println("清理后的状态:" + myNumbers); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值