- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
在 Java 标准库中,一个 LinkedHashSet
与HashSet
相同但具有可预测的迭代顺序(插入顺序)。我需要一个具有可预测迭代顺序(插入顺序)的 IdentityHashSet
(它使用对象标识或引用相等性,而不是对象相等性)。
虽然 Java 的标准库中没有 IdentityHashSet
,但您可以使用可用的 IdentityHashMap
轻松创建一个。使用以下语句:
Set<T> identitySet = java.util.Collections.newSetFromMap(new IdentityHashMap<>());
这几乎与 Guava 的 Sets.newIdentityHashSet()
中使用的实现相同;但这有一个未指定的,通常是困惑的顺序 HashMap
键(和 HashSet
元素)。
我搜索 IdentityLinkedHashSet
的实现没有结果,所以我决定自己实现它。我发现的一个有用结果是 this answer ,这建议在组合中使用身份包装类 LinkedHashSet
.
我试图实现这个想法。下面是我实现的一些关键片段。 Access the full Gist here .
public class IdentityLinkedHashSet<E> implements Set<E> {
private LinkedHashSet<IdentityWrapper> set;
/* ... constructors ... */
@Override
public boolean add(E e) {
return set.add(new IdentityWrapper(e));
}
@Override
public boolean contains(Object obj) {
return set.contains(new IdentityWrapper((E) obj));
}
/* ... rest of Set methods ... */
private class IdentityWrapper {
public final E ELEM;
IdentityWrapper(E elem) {
this.ELEM = elem;
}
@Override
public boolean equals(Object obj) {
return obj != null && ELEM == obj;
}
@Override
public int hashCode() {
return System.identityHashCode(ELEM);
}
}
}
然后我写了一些单元测试来验证我的实现。不幸的是,一些断言失败了!这是我的测试:
String str1 = new String("test-1");
String str2 = new String("test-2");
String str3 = new String("test-2");
Set<String> identitySet = new IdentityLinkedHashSet<>();
assertTrue(idSet.add(str1));
assertFalse(idSet.add(str1)); // <-- fails!
assertTrue(idSet.contains(str1)); // <-- fails!
//
assertTrue(idSet.add(str2));
assertFalse(idSet.add(str2)); // <-- fails!
assertTrue(idSet.contains(str2)); // <-- fails!
assertFalse(idSet.contains(str3));
//
assertTrue(idSet.add(str3));
assertFalse(idSet.add(str3)); // <-- fails!
assertTrue(idSet.contains(str3)); // <-- fails!
assertEquals(3, idSet.size()); // <-- fails!
我在这个实现中做错了什么?
最佳答案
调用 IdentityWrapper.equals()
时方法。 LinkedHashSet
不会通过 ELEM
而是一个 IdentityWrapper
对象。
你必须引入一个额外的检查(instanceOf
)然后打开传递的对象来比较元素:
public boolean equals(Object obj) {
return (obj instanceof IdentityLinkedHashSet<?>.IdentityWrapper) &&
ELEM == ((IdentityLinkedHashSet<?>.IdentityWrapper) obj).ELEM;
}
一些注意事项:
instanceof
将检查 null,因此您可以完全删除该检查。 您必须引用 IdentityWrapper
像这样:IdentityLinkedHashSet<?>.IdentityWrapper
因为它不是 static
类(class)。如评论中所述。它可以设为静态,ELEM
的类型可以从 E
更改至 Object
.你会留下一个更好的 equals
方法:
private static class IdentityWrapper {
public final Object ELEM;
IdentityWrapper(Object elem) {
this.ELEM = elem;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof IdentityWrapper) && ELEM == ((IdentityWrapper) obj).ELEM;
}
@Override
public int hashCode() {
return System.identityHashCode(ELEM);
}
}
关于java - 通过包装 LinkedHashSet 实现 IdentityLinkedHashSet,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54254292/
在 Java 标准库中,一个 LinkedHashSet与HashSet相同但具有可预测的迭代顺序(插入顺序)。我需要一个具有可预测迭代顺序(插入顺序)的 IdentityHashSet(它使用对象标
我是一名优秀的程序员,十分优秀!