Срок действия индекса Java LinkedHashSet

Я портировал большой кусок Java-кода на C++, и мне пришлось реализовать такие вещи, как LinkedHashSet, как я уже говорил. Я сделал разумное факсимиле LinkedHashSet/Map, используя многоиндексные контейнеры Boost.

Поскольку я портирую код, я сталкиваюсь с некоторыми интересными вещами с multi_index, так как содержащиеся объекты не являются изменяемыми (если вы не пометите определенные поля класса как изменяемые). Однако, если ключ вычисляется из некоторых изменяемых членов содержащегося в нем класса, все может стать интересным.

Чтобы прояснить некоторые вещи, я решил написать тривиальный пример на Java, чтобы проверить поведение их LinkedHashSet. Результаты немного удивляют меня; кажется, что они ведут себя как мультииндексные контейнеры Boost, в которых индексы не восстанавливаются при изменении содержимого объекта (как вы могли ожидать); однако компилятор ни на что не жалуется - кажется, очень легко выстрелить себе в ногу (код, который я портирую, кажется, совершает упомянутый грех, который знает, как он все еще работает).

Это всего лишь ограничение отсутствия 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, по крайней мере, вы должны собрать итератор, чтобы вызвать это!

2 ответа

Решение

Не рекомендуется использовать изменяемые объекты в Set (или как ключи в Map). В качестве Javadoc для Set говорит:

Примечание. Необходимо соблюдать осторожность, если в качестве элементов набора используются изменяемые объекты. Поведение набора не указывается, если значение объекта изменяется таким образом, что это влияет на сравнение равных, в то время как объект является элементом в наборе.

Так что ваш пример прямо на месте, и ставит Set в области "поведение... не указано".

Основная причина именно в том, что Пол Беллора говорит в своем ответе.

Обратите внимание, что LinkedHashSet продолжается HashSet который просто оборачивает HashMap это заботится только о его ключах. Так что мы на самом деле говорим здесь о поведении HashMap,

HashMap просто хранит ссылки на свои ключи и не отвечает за отслеживание изменений в этих объектах. Единственный раз, когда он вычисляет хэши ключей, это когда они put или когда HashMap изменяет размеры

Теоретически возможна пользовательская реализация HashMap, которая отслеживает изменения своих ключей, но это будет зависеть от ключей, реализующих интерфейс, который запускает уведомления при изменении их свойств. Тот факт, что этот пользовательский HashMap может использоваться только с ключом определенного типа, сделает его очень специализированным.

Другие вопросы по тегам