java - 通过包装 LinkedHashSet 实现 IdentityLinkedHashSet
问题描述
在 Java 标准库中,aLinkedHashSet
与 a 相同,HashSet
但具有可预测的迭代顺序(插入顺序)。我需要一个IdentityHashSet
具有可预测迭代顺序(插入顺序)的(使用对象标识或引用相等,而不是对象相等)。
虽然IdentityHashSet
Java 的标准库中没有,但您可以使用IdentityHashMap
以下语句轻松创建一个:
Set<T> identitySet = java.util.Collections.newSetFromMap(new IdentityHashMap<>());
这与 Guava 中使用的实现几乎相同Sets.newIdentityHashSet()
;但这具有未指定的,通常是混乱的HashMap
键(和HashSet
元素)顺序。
我对 an 实现的搜索IdentityLinkedHashSet
没有结果,所以我决定自己实现它。我发现的一个有用的结果是这个答案,它建议使用一个身份包装类与LinkedHashSet
.
我试图实现这个想法。下面是我的实现的一些关键片段。在此处访问完整的要点。
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
类。如评论中所述。可以将其设为静态,并且可以将其类型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); } }
推荐阅读
- c# - 如何通过单个字符的一次或多次出现来拆分字符串
- javascript - Highcharts:按系列名称设置单个文本颜色
- asp.net - 我所有的 Kentico 实时站点 404 和预览也是如此
- google-apps-script - 取消保护受保护的工作表,以便其他人可以运行脚本然后再次保护工作表
- c# - 调试引发异常的属性时显示异常消息
- scala - 未生成反向路由
- html - 在css中为不同的视频添加视频源
- python - Python:使用不同大小的数据框根据日期时间条件创建新列
- cypress - Cypress - 带有请求有效负载的 API 调用 - 如何在代码中处理有效负载
- coq - 对于 Coq 中的异构列表,是否可以证明与 Forall_inv 等效?