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 SecurityManagere que não há falhas.
Estou feliz com o lançamento método ConcurrentModificationException, NullPointerException, IllegalArgumentException, ClassCastException, etc., ou talvez mesmo pendurado.
Eu escolhi Stringcomo 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 ArrayListe TreeSet.)
NavigableSetoutras Comparablecoleçõ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 hashCodeimplementação dos elementos TreeSete PriorityQueuedepende da Comparator(e você nem pode crie uma cópia equivalente sem aceitar o comparador personalizado, se houver), EnumSetconfie na integridade do enumtipo específico que nunca é verificado após a compilação, para que um arquivo de classe, não gerado com javacou artesanal, possa subvertê-lo.
new TreeSet<>(strs)onde strsestá um NavigableSet. Esta não é uma cópia em massa, pois o resultado TreeSetusará 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 TreeSetcópia com comparador personalizado
checkcastpara cada elemento é toArraycom 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.