В прошлом я говорил, что для безопасного копирования коллекции сделайте что-то вроде:
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
определенный тип. Мы всегда на этом заканчиваем. Родовые коллекции даже не знают своего фактического типа элемента, поэтому их конструкторы копирования не могут предоставить аналогичную функциональность. Конечно, вы можете отложить проверку до правильного использования, но тогда я не знаю, на что направлены ваши вопросы. Вам не нужна «семантическая целостность», когда вы хорошо справляетесь с проверкой и сбоем непосредственно перед использованием элементов.