是否存在基于对象引用判定重复元素的Java集合实现?
你想要的是基于对象引用(而非值相等)判断重复的集合,Java标准库虽然没有直接提供名为IdentityHashSet的类,但可以通过现有API轻松构造出符合要求的实现,完全不需要自己从零开始写。
为什么HashSet不符合你的需求?
HashSet的重复判断依赖元素的equals()和hashCode()方法。对于Rectangle来说,它的equals()是比较矩形的几何属性(x、y坐标和宽高),所以初始状态下的r1和r2虽然是不同引用,但因为值相等,HashSet会认为它们是同一个元素,这就是你看到初始add(r2)返回false的原因。
解决方案:用Collections.newSetFromMap + IdentityHashMap
IdentityHashMap是Java提供的一个特殊Map实现,它用**对象引用相等(==)**来判断键的唯一性,而不是依赖equals()。我们可以用Collections.newSetFromMap()方法,把IdentityHashMap包装成一个Set,这个Set就会完全按照对象引用来判断重复。
修改后的代码示例
import java.awt.Rectangle; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Set; public class SetTest { public static void main(String[] args) { Rectangle r1 = new Rectangle(); Rectangle r2 = new Rectangle(); // 构造基于身份的Set Set<Rectangle> set = Collections.newSetFromMap(new IdentityHashMap<>()); System.out.println("Add r1 : " + set.add(r1)); System.out.println("Contains r2 : " + set.contains(r2)); System.out.println("Add r2 : " + set.add(r2)); System.out.println(); r2.grow(100,100); System.out.println("Grow r2"); System.out.println("Contains r2 : " + set.contains(r2)); System.out.println("Add r2 : " + set.add(r2)); System.out.println("Contains r2 : " + set.contains(r2)); System.out.println(); r2.grow(100,100); System.out.println("Grow r2"); System.out.println("Contains r2 : " + set.contains(r2)); } }
运行结果(完全符合你的期望)
Add r1 : true
Contains r2 : false
Add r2 : trueGrow r2
Contains r2 : true
Add r2 : false
Contains r2 : trueGrow r2
Contains r2 : true
额外说明
这个实现的核心特性:
- 仅当两个对象是同一个引用(
obj1 == obj2)时,才会被视为重复元素 - 即使对象的内部状态(比如
Rectangle的大小)改变,只要引用不变,集合依然能正确识别它(因为IdentityHashMap使用的是System.identityHashCode(),这个值在对象生命周期内不会改变)
如果需要更直观的类名,你也可以封装一个自己的IdentityHashSet:
import java.util.AbstractSet; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Set; public class IdentityHashSet<E> extends AbstractSet<E> { private final Set<E> delegateSet; public IdentityHashSet() { delegateSet = Collections.newSetFromMap(new IdentityHashMap<>()); } @Override public boolean add(E e) { return delegateSet.add(e); } @Override public boolean contains(Object o) { return delegateSet.contains(o); } @Override public Iterator<E> iterator() { return delegateSet.iterator(); } @Override public int size() { return delegateSet.size(); } // 根据需求重写其他Set方法即可 }
这样你就可以直接使用IdentityHashSet<Rectangle> set = new IdentityHashSet<>();,和普通Set的使用方式完全一致。
内容的提问来源于stack exchange,提问作者bitwiseWho

