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




Для этой цели вы можете использовать CopyOnWriteArrayList или ConcurrentLinkedQueue. Однако, если у вас много записей, подход CopyOnWrite будет дорогостоящим.
Если вы хотите удалить дубликаты, подумайте об использовании CopyOnWriteArraySet.
Использование связанного списка для удаления дубликатов - это буквально (и да, я имею в виду буквально не виртуально) худшая из возможных реализаций.
Никогда не упоминал об удалении дубликатов.
Классы коллекции в пакете 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.
Что вы делаете с НАСТОЯЩИМ объектом? Если нам нужно сделать этот дополнительный шаг только для того, чтобы использовать карту, разве не будет более подходящей другой структурой данных?
Используйте ConcurrentHashMap, но если все ваши строки относительно короткие (я бы сказал, менее 100 символов), очень маловероятно, что это даст вам заметное улучшение производительности - это может быть даже хуже, чем однопоточная реализация.