Я переносил большой кусок кода Java на C++, и мне приходилось реализовывать такие вещи, как LinkedHashSet, когда я ушел. Я сделал приемлемое факсимиле LinkedHashSet/Map, используя мультииндексные контейнеры Boost.
Когда я переношу код, я сталкиваюсь с некоторыми интересными вещами с multi_index, поскольку содержащиеся объекты не изменяемы (если только вы не пометите определенные поля класса как изменяемые). Однако, если ключ вычисляется из некоторых изменяемых членов содержащегося класса, все может стать интереснее.
Чтобы прояснить некоторые вещи, я подумал, что напишу тривиальный пример на Java, чтобы проверить поведение их LinkedHashSet. Результаты меня немного удивляют; кажется, что они ведут себя как контейнеры Boost Multi Index в том смысле, что индексы не регенерируются при изменении содержащегося объекта (как и следовало ожидать); однако компилятор никоим образом не жалуется - кажется, очень легко выстрелить себе в ногу (код, который я портирую, похоже, совершает упомянутый грех, кто знает, как он все еще работает).
Это просто ограничение отсутствия const_iterators в Java, или мне удалось сделать что-то особенно глупое или хитрое?
Вот тривиальный пример:
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
Интересно, что он знает, что Баз изменился; однако он все еще не находит Baz2.
Очевидно, это надумано, но очень правдоподобный код, на который я смотрю, кажется (через несколько косвенных действий) вызывает эту проблему. С Boost Multi Index, по крайней мере, вам нужно использовать итератор const, чтобы вызвать это!