Раніше я казав, що безпечно копіювати колекцію, роблячи щось на кшталт:
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
з певним типом. Ми завжди на цьому закінчуємося. Узагальнені колекції навіть не знають свого фактичного типу елементів, тому їх конструктори копій не можуть забезпечити подібну функціональність. Звичайно, ви можете відкласти будь-яку перевірку до прямого використання, але тоді я не знаю, на що спрямовані ваші запитання. Вам не потрібна "смислова цілісність", коли ви добре перевіряєте і не працюєте безпосередньо перед використанням елементів.