Java中HashSet存储ArrayList对象的内部机制?为何示例代码输出为1?
为什么HashSet存储两个内容相同的ArrayList后size是1?
这问题特别典型,我来给你一步步理清楚:
首先得搞懂HashSet的核心判断逻辑
HashSet不会直接存储对象的哈希值,它的去重逻辑依赖两个方法的配合:
- 第一步:调用对象的
hashCode()方法计算哈希值,找到对应的存储桶; - 第二步:在对应桶里,用
equals()方法和已有的元素逐一比较; - 只有当两个对象的hashCode相等,并且equals返回true时,HashSet才会判定这是同一个元素,拒绝重复添加。
再看ArrayList的equals()和hashCode()实现
ArrayList作为Java集合框架的一员,它的这两个方法是基于集合内容实现的,而非对象本身的内存地址:
equals():会遍历两个列表的所有元素,逐个用元素的equals()比较,只要元素数量相同、顺序相同、每个对应位置的元素都相等,就返回true;hashCode():根据列表里所有元素的哈希值计算得出,核心逻辑大概是:
所以只要两个ArrayList的内容完全一致,它们的hashCode值肯定相同。int hashCode = 1; for (E e : list) { hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode()); }
回到你的代码场景
你的代码里:
list1和list2是两个不同的对象(内存地址不同),但它们的内容完全一致:都包含Integer(1)和Integer(2),顺序也一样;- 所以
list1.equals(list2)返回true,两者的hashCode()值也完全相等; - 当你先把
list1加入HashSet,再添加list2时,HashSet发现它和已有的list1满足“hashCode相等+equals返回true”的重复判定条件,就不会把list2加入集合,最终set的size就是1。
额外补充:如果想让它们被当成不同元素怎么办?
如果你的业务场景需要把“内容相同但内存地址不同的ArrayList”当成不同元素存入HashSet,那你可以自定义一个继承ArrayList的子类,重写equals()和hashCode(),让它们基于对象的身份(比如用System.identityHashCode(this))来计算,举个例子:
class IdentityArrayList<E> extends ArrayList<E> { @Override public boolean equals(Object o) { return this == o; } @Override public int hashCode() { return System.identityHashCode(this); } }
用这个子类创建list1和list2,再存入HashSet,size就会是2了——不过这种做法很少见,一般只有特殊业务需求才会这么做。
内容的提问来源于stack exchange,提问作者Ganesh Chowdhary Sadanala
相关产品推荐
相关产品推荐

