ArrayList - добавить «одинаковые» объекты (same => equals, hashCode), Threads - PullRequest
9 голосов
/ 26 мая 2011

У меня есть один вопрос. Что происходит, когда я пытаюсь добавить «один и тот же» объект дважды в ArrayList. Под «тем же самым» я подразумеваю объект отдельного класса, который идентифицируется как одинаковый с помощью equals () и hashCode (). Он имеет разные значения для большинства переменных-членов и был создан, возможно, из разных потоков, но для equals () и hashCode () это "то же самое". Затем второй объект заменяет первый объект?

Кроме того, что произойдет, если два потока попытаются одновременно добавить эти объекты в ArrayList? Это вообще возможно? Если да, что происходит?

Спасибо! :-)

[EDIT] Спасибо за все ответы! Должен ли я использовать synchronizedList, а не использовать "synchronize (list) {}"? -> Я прочитал документы, даже с synchronizedList, для итерации синхронизации (список) должны быть использованы

[EDIT2] Может ли synchronizedList быть объявлен как переменная-член? Я пытался, но это не сработало.

Ответы [ 5 ]

15 голосов
/ 26 мая 2011

Нет, ArrayList вообще не пытается обнаружить дубликаты - вы можете иметь ArrayList с одной и той же ссылкой, появляющейся несколько раз.Если вы хотите, чтобы коллекция избегала дублирования, вам нужна реализация Set - и если вы также хотите сохранить порядок вставки, вы, вероятно, захотите LinkedHashSet.

Обратите внимание, однако, что без блокировки ArrayList должен не быть в первую очередь мутировавшим из нескольких потоков - это просто не означает, что в этом случае потокобезопасный набор не будет.Несколько потоков могут читать из ArrayList без синхронизации, но не изменять его.Из документов:

Обратите внимание, что эта реализация не синхронизирована.Если несколько потоков обращаются к экземпляру ArrayList одновременно, и хотя бы один из потоков структурно изменяет список, он должен быть синхронизирован извне.(Структурная модификация - это любая операция, которая добавляет или удаляет один или несколько элементов или явно изменяет размер базового массива; просто установка значения элемента не является структурной модификацией.) Обычно это выполняется путем синхронизации с некоторым объектом, который естественным образом инкапсулируетсписок.Если такого объекта не существует, список следует «обернуть» с помощью метода Collections.synchronizedList.Это лучше всего делать во время создания, чтобы предотвратить случайный несинхронизированный доступ к списку

Если вы хотите изменить коллекцию из нескольких потоков без блокировки, я предлагаю вам взглянуть на коллекции в java.util.concurrent.

6 голосов
/ 26 мая 2011

Затем второй объект заменяет первый объект?

Нет, большинство разработчиков делают явные проверки

if(!list.contains(foo)){
    list.add(foo);
}

Кроме того, что происходит, если два потока пытаются добавить эти объекты именно на в то же время в ArrayList? Это даже возможно? Если да, что происходит?

Да, это возможно. Если несколько потоков записывают / читают из одного и того же ArrayList, используйте ключевое слово synchronized при каждом обращении к этому списку

public List<Foo> getFoos(){
    synchronized(list){
        return list;
    }
}

public void addFoo(Foo foo){
    synchronized(list){
        list.add(foo);
    }
}

EDIT

Как кто-то указал, я полагаю, проверка того, содержит ли ArrayList объект, который нужно добавить, довольно дорогая. Если вы хотите, чтобы объект был добавлен только один раз, я бы следовал приведенной ниже рекомендации по использованию LinkedHashSet . Согласно API, при попытке добавить к этой структуре данных это

Добавляет указанный элемент в этот набор если его еще нет. Больше формально добавляет указанный элемент e к этому набору, если этот набор не содержит элемент e2 такой, что (e == null? e2 == ноль: e.equals (e2)). Если этот набор уже содержит элемент, вызов оставляет набор без изменений и возвращает ложь.

4 голосов
/ 26 мая 2011

Это позволит просто добавить. Список не имеет ничего общего с hashCode(), equals(), в то время как вставка не имеет значения для дубликата.

ArrayList не является потокобезопасным, поэтому вы можете не получить желаемого результата. Вы можете иметь synchronizedList от Collections класс

2 голосов
/ 26 мая 2011

ArrayList может содержать несколько ссылок на один и тот же точный объект (идентичность идентичности). Он не проверяет equals() или hashCode() при добавлении объектов.

Вы просто получите две ссылки в вашем ArrayList.

ArrayList НЕ является потокобезопасным ... поэтому поведение, если вы попытаетесь добавить два потока одновременно, не определено. Возможно, попробуйте использовать SynchronizedList, если вы хотите сделать что-то подобное.

1 голос
/ 26 мая 2011

Если вы попытаетесь добавить один и тот же объект дважды, он будет работать, или если вы попытаетесь добавить 2 объекта со всем одинаковым, он все равно будет работать.Это не лучшая практика, потому что вести список труднее.

в целом: не стоит этого делать

...