【发布时间】:2014-09-26 20:52:24
【问题描述】:
我知道这个问题已经在 SO 上被问过几次,但我仍然没有找到令人满意的解决方案,而且我不确定该走哪条路。问题是:
为什么 Java 库不提供 HashSet.get(Object o) 和 HashMap.getKey(Object o) 方法来返回映射中的实际实例以提供相等的实例?示例:
// Retrieve a house with ID=10 that contains additional information like size,
// location and price.
houses.get(new House(10));
我认为最好的答案可以在here 找到。所以这是我知道的混合答案:
既然已经有了实例,为什么还需要它?尝试获取您已经拥有的相同对象是没有意义的。该对象有一个标识符(控制它与其他 Foo 类型的相等性)以及任何数量的其他字段,这些字段不影响它的身份。我希望能够通过构造一个“相等”的 Foo 对象(文本取自其中一个 cmets)从 Set 中获取对象(其中包括额外的字段)。 -> 没有答案
迭代
Collection并使用equals()搜索实例。 这使用线性搜索,在大型集合中非常慢。 -> 不好的答案使用 HashMap 代替 HashSet 我不需要映射,而且我认为在
getHouses()这样的方法中返回映射是不够的。 getter 应该返回Set而不是Map。使用
TreeSet.ceiling- 不知道下面的这个 hacky 代码(仅限 Java 8
HashSet)使用反射并提供了缺失的功能。我在其他答案中没有找到类似的东西(不足为奇)。如果定义了目标 Java 版本并且未来的 Java 版本最终会提供这样的方法,这可能是一个可接受的解决方案,现在我们有接口的default方法。可以想到default E get(E o){stream().filter(e->e.equals(o)).findAny().orElse(null);}
// Alternative: Subclass HashSet/HashMap and provide a get()/getKey() methods
public static <T> T getFromSet(HashSet<T> set, T key) throws Exception {
Field mapField = set.getClass().getDeclaredField("map");
mapField.setAccessible(true);
HashMap<T, Object> map = (HashMap) mapField.get(set);
Method getNodeMethod = map.getClass().getDeclaredMethod("getNode",
int.class, Object.class);
getNodeMethod.setAccessible(true);
return (T) ((Map.Entry) getNodeMethod.invoke(map, key.hashCode(),
key)).getKey();
}
以下是问题:
- 最好的解决方案是使用
HashMap<House, House>而不是HashSet<House>? - 是否有其他库提供此功能并支持并发访问?
- 您知道解决此功能的错误吗?
关于 SO 的类似问题:
【问题讨论】:
-
恕我直言,在这个陈述中思考“对象有一个标识符(它控制它与其他 Foo 类型的相等性)加上任何数量的其他不影响它的身份的字段”——我想你这里真正需要的是一个以该标识符为键、对象为值的 Map。
-
你有没有想过不是这个世界完全疯了,而是你的系统设计错了?如果两个实体相等,则意味着它们不仅可以互换,而且实际上彼此无法区分。这就是我们所说的两件事相等的意思。如果在您的设计中并非如此,则意味着您正在滥用
equals()方法。 -
@biziclop [
equals() == true=> 这些实例彼此之间没有区别] 这对于java.*中的许多类来说并非如此。我宁愿说,如果两个对象引用同一个现实世界的实例,则它们是相等的。 -
@biziclop 这是一个例子:
HttpCookie c1 = new HttpCookie("name", "value"); c1.setComment("c1"); HttpCookie c2 = new HttpCookie("name", "value"); c2.setComment("c2"); System.out.printf("c1.comment=%s, c2.comment=%s, c1.equals(c2)=%b\n", c1.getComment(), c2.getComment(), c1.equals(c2)); -
@biziclop 我正在使用 House 创建一个示例,该示例显示了从
Set检索元素的原理,仅此而已。我正在处理的数据完全不同,它们的标识和可选值都在 RFC 中定义。正如我所说:当它们引用同一个现实世界对象时,两个实例应该相等。 ...所以现在我很好奇:你会在equals()中包含评论吗?您自相矛盾:这 2 个 Cookie 应该被认为是平等的,但可以相互区分。
标签: java collections