Поточно-безопасная структура данных для проверки существования и записи, если нет

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

Я забываю, какая структура данных для этого подходит. Все, что есть в Java.util, нормально, как и высокопроизводительные сторонние библиотеки.

Используйте ConcurrentHashMap, но если все ваши строки относительно короткие (я бы сказал, менее 100 символов), очень маловероятно, что это даст вам заметное улучшение производительности - это может быть даже хуже, чем однопоточная реализация.

Voo 23.06.2018 17:00
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
1
435
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Для этой цели вы можете использовать CopyOnWriteArrayList или ConcurrentLinkedQueue. Однако, если у вас много записей, подход CopyOnWrite будет дорогостоящим.

Если вы хотите удалить дубликаты, подумайте об использовании CopyOnWriteArraySet.

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

Voo 23.06.2018 16:59

Никогда не упоминал об удалении дубликатов.

user2355058 23.06.2018 17:08
Ответ принят как подходящий

Классы коллекции в пакете java.util не являются потокобезопасными, чтобы обеспечить максимальную производительность в однопоточных приложениях. (Вектор и Hashtable являются исключениями)

Есть несколько способов добиться требуемой потоковой безопасности.

Синхронизированная оболочка Set<String> safeSet = Collections.synchronizedSet(new HashSet<>());

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

java.util.concurrent Пакет

Java 5 представила параллельные коллекции, которые обеспечивают гораздо лучшую производительность, чем синхронизированные оболочки.

Существуют разные варианты: копирование при записи, сравнение и замена и параллельные коллекции.

Параллельные коллекции используют специальную блокировку, которая более гибкая, чем синхронизация.

Итак, для того, что вы делаете, HashSet, вероятно, будет хорошим выбором, если он будет однопоточным. В параллельном пакете вы можете использовать ConcurrentHashMap.

Это выглядело бы так:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

...

 private static final Object PRESENT = new Object();
 Map<String, Object> seenStrings = new ConcurrentHashMap<>();



for ( String aString : stringList ) {
    if ( seenStrings.containsKey(aString) ) {
        // Already there
    } else {
        // Not seen yet
        seenStrings.put(aString, PRESENT);
    }
}

Обновлять Комментарий Энди хороший, я не был уверен, что вы хотите сделать, если вы уже видели предмет или нет.

Вы можете сделать это, чтобы проверка и вставка выполнялись атомарно.

if (seenStrings.put(aString, PRESENT) == null) {
       // Not seen yet
} 

Обновлять В Java 8+ вы можете создать набор, поддерживаемый указанной картой. Фактически ConcurrentHashSet.

Set<String> seenStrings = Collections.newSetFromMap(new ConcurrentHashMap<>());
for (String aString : stringList) {
    if (seenStrings.add(aString)) {               
            // Not seen yet
    }
}

"это будет выглядеть так", надеюсь, нет. ConcurrentHashSet не волшебный: он не может знать, что отдельные вызовы, сделанные к нему, должны выполняться атомарно (в частности: использование contains и add, как это, не работает). Вместо этого используйте if (!seenStrings.add(aString)) и удалите else.

Andy Turner 23.06.2018 17:21

Что вы делаете с НАСТОЯЩИМ объектом? Если нам нужно сделать этот дополнительный шаг только для того, чтобы использовать карту, разве не будет более подходящей другой структурой данных?

user2355058 23.06.2018 18:33

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