我必须实现一个实例彼此具有双向关系的类。例如,我有类FooBar
,它应该提供方法sameAs(FooBar x)
并为包含其等效实例的每个实例维护一个Set。因此,如果我致电foo.sameAs(bar)
,则foo
中的设置应包含bar
,反之亦然。当然,调用bar.sameAs(foo)
不起作用。
为了澄清:此类的实例仅语义相等。 equals
仍应返回false
。
我提出的解决方案是要实现从internalSameAs(FooBar x)
调用的私有方法sameAs(FooBar x)
,要么使用静态方法sameAs(FooBar x, FooBar y)
。
解决方案1:
class FooBar {
Set<FooBar> sameAs = new HashSet<FooBar>();
public void sameAs(FooBar x) {
this.internalSameAs(x);
x.internalSameAs(this);
}
public void internalSameAs(FooBar x) {
sameAs.add(x);
}
}
解决方案2:
class FooBar {
Set<FooBar> sameAs = new HashSet<FooBar>();
public static void sameAs(FooBar x, FooBar y) {
x.sameAs.add(y);
y.sameAs.add(x);
}
}
您更喜欢哪一个?为什么?还是有另一种我没想过的方式?
答案 0 :(得分:2)
您使用的命名令人困惑。 sameAs
听起来好像是一个应该返回一个布尔值的测试,但是从你的代码来看它似乎更恰当地命名为declareSameAs
。当您致电foo.sameAs(bar)
时,您声明foo
和bar
是相同的,没有进行测试,是否正确?
问题是您的代码可以声明
x.sameAs(y);
y.sameAs(z);
但不会出现x与z相同的情况,这可能不是你想要的(如果它是你想要的,你肯定需要更改方法名称)。
在我看来,您希望将实例划分为集合,并让每个实例保持对其所在集合的引用(不到实例内部的单独集合)。当您声明两个实例相同时,需要组合这些集合,并确保所有受影响的实例都引用了组合集。
答案 1 :(得分:1)
您是否可以灵活使用要使用的数据结构?如果是这样,您可以使用Multimap
(来自Guava Collections),它在类FooBar
的所有实例中都是静态的。在Multimap
中,您可以将键作为FooBar
引用(如果有的话,可以是唯一的ID),并且值将是FooBar
s的引用(或id.s)具有sameAs
关系。
答案 2 :(得分:1)
您的“双向”samesAs(...)
方法听起来像Object.equals(...)
,根据javadoc,它是“非空对象引用的等价关系”。如果这是您想要的,那么您只需要覆盖班级中的equals
。
当你说“FooBar
应该为包含其等效实例的每个实例保留一个Set”时,我有点迷失了。如果你想为FooBar
对象构建等价的类,那么我认为使用java Collection
来表示它们是个好主意,更准确地说是Set
。
这是一个很快被黑客攻击的例子:
public class FooBar {
@Override
public boolean equals(Object other) {
// do whatever fancy computation to determine if
// the object other is equal to this object
}
}
和等效类:
@SuppressWarnings("serial")
public class FooBarEquivalentClass extends HashSet<FooBar> {
@Override
public boolean add(FooBar e) {
if (isEmpty())
return super.add(e);
else if (e.equals(iterator().next()))
return super.add(e);
else
return false;
}
}
答案 3 :(得分:1)
也许有不同的方式:sameAs
听起来与equals
非常相似。如果我们不需要equals
用于其他内容,那么我只需在equals
上实施FooBar
方法,以便我们只需执行
if (foo.equals(bar))
System.out.println("We're equal (aka: 'equivalent/the same')");
在这种情况下,我们不需要任何设置 - 只需要一个规则来确定,如果两个实例相等。
您可以将相同性信息存储在这些类之外的单独数据结构中。中央地图可以完成这项工作:
HashMap<FooBar, Set<FooBar>> sameFooBars;
如果你有“相同”的对象,只需将它们添加到地图:
public static void addSameObjects(FooBar foo1, FooBar foo2) {
Set<FooBar> set = getMap().get(foo1);
if (set == null) {
set = new HashSet<FooBar>();
getMap().put(foo1, set);
}
set.add(foo2);
// serious implementation avoid code duplication...
set = getMap().get(foo2);
if (set == null) {
set = new HashSet<FooBar>();
getMap().put(foo2, set);
}
set.add(foo1);
}
测试:
public static boolean isSame(FooBar foo1, FooBar foo2) {
if (getMap().get(foo1) == null)
return false;
return getMap().get(foo1).contains(foo2);
}
答案 4 :(得分:1)
您真的需要在所有对象中维护一个等价列表吗?如果可能的话,我会将对等集与对象本身分开。这将更容易维护。
然后你可以使用@posdef的多重图或更简单的一个Map&gt;继续使用标准的JAVA API。
答案 5 :(得分:0)
“与”相同但不“等于”听起来应该使用Comparable
。
我认为将compareTo()
或sameAs()
实现为实例方法而不是静态更有意义,因为您总是需要两个真实实例来进行任何比较。
答案 6 :(得分:0)
听起来你想要的是将等价组与对象实例分开。
制作地图&lt; FooBar,Set&lt; FooBar&gt;&gt;并注意,当您查找对象时,该集将包含其自身。