No passado, eu disse para copiar com segurança uma coleção, algo como:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
ou
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Mas esses construtores de "cópia", métodos e fluxos de criação estática semelhantes, são realmente seguros e onde as regras são especificadas? Por segurança, quero dizer, são as garantias básicas de integridade semântica oferecidas pela linguagem Java e coleções impostas a um chamador mal-intencionado, assumindo o backup de um razoável SecurityManager
e que não há falhas.
Estou feliz com o lançamento método ConcurrentModificationException
, NullPointerException
, IllegalArgumentException
, ClassCastException
, etc., ou talvez mesmo pendurado.
Eu escolhi String
como exemplo de um argumento de tipo imutável. Para esta pergunta, não estou interessado em cópias profundas para coleções de tipos mutáveis que têm suas próprias dicas.
(Para ser claro, observei o código-fonte do OpenJDK e tenho algum tipo de resposta para ArrayList
e TreeSet
.)
NavigableSet
outras Comparable
coleções baseadas podem às vezes detectar se uma classe não é implementada compareTo()
corretamente e gerar uma exceção. Não está claro o que você quer dizer com argumentos não confiáveis. Você quer dizer que um malfeitor cria uma coleção de Strings ruins e quando você as copia para sua coleção algo ruim acontece? Não, a estrutura de coleções é bastante sólida, existe desde a versão 1.2.
HashSet
(e todas as outras coleções de hash em geral) depende da correção / integridade da hashCode
implementação dos elementos TreeSet
e PriorityQueue
depende da Comparator
(e você nem pode crie uma cópia equivalente sem aceitar o comparador personalizado, se houver), EnumSet
confie na integridade do enum
tipo específico que nunca é verificado após a compilação, para que um arquivo de classe, não gerado com javac
ou artesanal, possa subvertê-lo.
new TreeSet<>(strs)
onde strs
está um NavigableSet
. Esta não é uma cópia em massa, pois o resultado TreeSet
usará o comparador da fonte, que é até necessário para manter a semântica. Se você está bem apenas processando os elementos contidos, toArray()
é o caminho a percorrer; ele ainda manterá a ordem da iteração. Quando você está bem com “pegar elemento, validar elemento, usar elemento”, você nem precisa fazer uma cópia. Os problemas começam quando você deseja verificar todos os elementos, seguido pelo uso de todos os elementos. Então você não pode confiar em uma TreeSet
cópia com comparador personalizado
checkcast
para cada elemento é toArray
com um tipo específico. Estamos sempre terminando. As coleções genéricas nem conhecem seu tipo de elemento real, portanto, seus construtores de cópias não podem fornecer uma funcionalidade semelhante. Obviamente, você pode adiar qualquer verificação para o uso anterior correto, mas não sei o que suas perguntas estão buscando. Você não precisa de "integridade semântica" quando estiver bem em verificar e falhar imediatamente antes de usar elementos.