我一直在将大量 Java 代码移植到 C++ 中,并且在我离开时不得不实现 LinkedHashSet 之类的东西。我使用 Boost 的多索引容器对 LinkedHashSet/Map 进行了合理的复制。
当我移植代码时,我遇到了一些有趣的东西,其中包含 multi_index,因为包含的对象不是可变的(除非您将类的特定字段标记为可变)。但是,如果密钥是从包含的类的一些可变成员中计算出来的,那么事情就会变得有趣。
为了澄清一些事情,我想我会用 Java 写一个简单的例子来检查他们的 LinkedHashSet 的行为。结果让我有点惊讶;看起来它们的行为类似于 Boost 的多索引容器,因为在修改包含的对象时不会重新生成索引(如您所料);然而,编译器并没有以任何方式抱怨——似乎很容易让自己陷入困境(我正在移植的代码似乎犯了上述罪过,谁知道它仍然是如何工作的)。
这只是 Java 中缺少 const_iterators 的限制,还是我设法做了一些特别愚蠢或棘手的事情?
这是一个简单的例子:
class StringContainer
{
public String s;
public StringContainer(String s)
{
this.s = s;
}
public boolean equals(Object t1)
{
StringContainer other = (StringContainer) t1;
return this.s == other.s;
}
public int hashCode()
{
int val = 8;
for (int i = 0; i < s.length(); i++)
val += s.charAt(i);
return val;
}
public String toString()
{
return s;
}
}
class test
{
public static void main(String[] args)
{
Set<StringContainer> set = new LinkedHashSet();
set.add(new StringContainer("Foo"));
set.add(new StringContainer("Bar"));
set.add(new StringContainer("Baz"));
set.add(new StringContainer("Qux"));
Iterator<StringContainer> it = set.iterator();
while (it.hasNext())
{
StringContainer s = it.next();
if (s.s == "Baz")
s.s = "Baz2";
System.out.println(s);
}
System.out.println("\nRe-iterate:\n");
it = set.iterator();
while (it.hasNext())
{
StringContainer s = it.next();
System.out.println(s);
}
System.out.println();
if (set.contains(new StringContainer("Foo")))
System.out.println("Contains Foo");
if (set.contains(new StringContainer("Baz")))
System.out.println("Contains Baz");
else
System.out.println("Does not contain Baz");
if (set.contains(new StringContainer("Baz2")))
System.out.println("Contains Baz2");
else
System.out.println("Does not contain Baz2");
}
}
它打印出以下内容:
Foo
Bar
Baz2
Qux
Re-iterate:
Foo
Bar
Baz2
Qux
Contains Foo
Does not contain Baz
Does not contain Baz2
有趣的是,它知道 Baz 变了;但是它仍然没有找到 Baz2。
显然这是人为的,但我正在查看的非常合理的代码似乎(通过多个间接)导致了这个问题。使用 Boost Multi Index 至少你必须 const-cast 一个迭代器来导致这个!