You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

何种情况下交换a、b添加顺序后hashSet.size()为2而非3?

问题拆解与解答

嘿,这个问题挺有意思的,咱们一步步理清楚:要让交换add(a)和add(b)的顺序后,HashSet的大小从3变成2,核心得抓住HashSet的本质——它靠元素的hashCode()和equals()方法判断是否重复,而且如果元素的属性(影响这两个方法的)在添加过程中发生变化,就会搞出“顺序影响结果”的情况。

具体得满足这几个条件:

  • 初始状态下,a、b、c三个对象两两都不“相等”(也就是a.equals(b)、a.equals(c)、b.equals(c)全返回false),所以原顺序添加后集合能装下3个元素,size是3。
  • a的equals()或者hashCode()逻辑依赖某个可变属性,而且这个属性会被b的添加操作(或者b本身的存在)给修改。
  • 当先加b再加a时,a的那个可变属性已经被修改了,导致此时a和b被判定为“相等”(哈希值相同且equals返回true),所以加a的时候会被当成重复元素,集合不新增;而c还是和a、b都不相等,最后集合size就变成2了。

给你写个实际能跑的代码例子,一看就懂:

import java.util.HashSet;

class ThirdPartyType {
    // 用一个共享计数器当可变属性
    private static int sharedFlag = 0;
    private int id;

    public ThirdPartyType(int id) {
        this.id = id;
    }

    // 自定义equals:当flag为1时,id=1的a会和id=2的b判定为相等
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ThirdPartyType that = (ThirdPartyType) o;
        if (sharedFlag == 1 && this.id == 1 && that.id == 2) {
            return true;
        }
        return id == that.id;
    }

    // 自定义hashCode:flag为1时,a的哈希值和b一样
    @Override
    public int hashCode() {
        if (sharedFlag == 1 && id == 1) {
            return 2;
        }
        return id;
    }

    // 模拟添加b时触发flag修改
    public void triggerFlag() {
        if (id == 2) {
            sharedFlag = 1;
        }
    }
}

public class TestHashSet {
    public static void main(String[] args) {
        ThirdPartyType a = new ThirdPartyType(1);
        ThirdPartyType b = new ThirdPartyType(2);
        ThirdPartyType c = new ThirdPartyType(3);

        // 原顺序:先加a,再加b,最后加c
        HashSet<ThirdPartyType> originalSet = new HashSet<>();
        originalSet.add(a);
        b.triggerFlag(); // 加b的时候改flag,但a已经加进去了,不影响之前的判断
        originalSet.add(b);
        originalSet.add(c);
        System.out.println(originalSet.size()); // 输出3,因为加a的时候flag还是0,a和b不重复

        // 交换顺序:先加b,再加a,最后加c
        ThirdPartyType.sharedFlag = 0; // 重置flag
        HashSet<ThirdPartyType> swappedSet = new HashSet<>();
        b.triggerFlag(); // 先改flag,再把b加进去
        swappedSet.add(b);
        swappedSet.add(a); // 此时a的哈希和b一样,equals也返回true,被判定为重复,加不进去
        swappedSet.add(c);
        System.out.println(swappedSet.size()); // 输出2
    }
}

总结一下就是:后添加的对象会改变先添加对象的重复判断逻辑,但原顺序里先加的对象还没被修改,所以三个都能存;交换顺序后,先加的对象提前修改了逻辑,导致后面加的对象被当成重复的。

内容的提问来源于stack exchange,提问作者Руслан Сударев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:56:09