В прошлом я говорил, что для безопасного копирования коллекции сделайте что-то вроде:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
или
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Но действительно ли эти «копирующие» конструкторы, подобные статические методы создания и потоки действительно безопасны и где указаны правила? Под безопасностью я подразумеваю базовые гарантии семантической целостности, предлагаемые языком Java и коллекциями, которые применяются против злонамеренного вызывающего, при условии, что они подкреплены разумными данными SecurityManagerи что в них нет недостатков.
Я доволен метод метания ConcurrentModificationException, NullPointerException, IllegalArgumentException, ClassCastExceptionи т.д., или , возможно , даже висит.
Я выбрал Stringв качестве примера аргумент неизменного типа. На этот вопрос меня не интересуют глубокие копии коллекций изменяемых типов, которые имеют свои собственные ошибки.
(Чтобы было ясно, я посмотрел исходный код OpenJDK и у меня есть какой-то ответ за ArrayListи TreeSet.)
NavigableSetи другие Comparableоснованные коллекции могут иногда обнаруживать, что класс не реализуется compareTo()правильно, и генерировать исключение. Немного неясно, что вы подразумеваете под ненадежными аргументами. Вы имеете в виду, что злодей создает коллекцию плохих строк, и когда вы копируете их в свою коллекцию, происходит что-то плохое? Нет, структура коллекций довольно прочная, она существует примерно с 1.2.
HashSet(и все другие коллекции хеширования в целом), полагаясь на правильность / целостность hashCodeреализации элементов, TreeSetи PriorityQueueзависеть от Comparator(и вы даже не можете создает эквивалентную копию без принятия пользовательского компаратора, если таковой имеется), EnumSetдоверяет целостности определенного enumтипа, который никогда не проверяется после компиляции, поэтому файл класса, не созданный с помощью javacили созданный вручную, может подорвать его.
new TreeSet<>(strs)где strsнаходится NavigableSet. Это не массовая копия, поскольку в результате TreeSetбудет использован компаратор источника, который даже необходим для сохранения семантики. Если вам достаточно просто обработать содержащиеся элементы, toArray()это путь; он даже будет поддерживать порядок итераций. Когда у вас все в порядке с «взять элемент, проверить элемент, использовать элемент», вам даже не нужно делать копию. Проблемы начинаются, когда вы хотите проверить все элементы, после чего используются все элементы. Тогда вы не можете доверять TreeSetкопии с пользовательским компаратором
checkcastдля каждого элемента, имеет toArrayопределенный тип. Мы всегда на этом заканчиваем. Родовые коллекции даже не знают своего фактического типа элемента, поэтому их конструкторы копирования не могут предоставить аналогичную функциональность. Конечно, вы можете отложить проверку до правильного использования, но тогда я не знаю, на что направлены ваши вопросы. Вам не нужна «семантическая целостность», когда вы хорошо справляетесь с проверкой и сбоем непосредственно перед использованием элементов.