W przeszłości mówiłem, aby bezpiecznie skopiować kolekcję i zrobić coś takiego:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
lub
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Ale czy te konstruktory „kopiujące”, podobne metody i strumienie tworzenia statycznego są naprawdę bezpieczne i gdzie określone są reguły? Rozumiem przez to, że podstawowe gwarancje integralności semantycznej oferowane przez język Java i kolekcje są wymuszane przeciwko złośliwemu rozmówcy, przy założeniu, że są uzasadnione SecurityManager
i że nie ma żadnych wad.
Jestem zadowolony z metody rzucania ConcurrentModificationException
, NullPointerException
, IllegalArgumentException
, ClassCastException
, itd., A może nawet powieszenie.
String
Jako przykład argumentu niezmiennego wybrałem . W przypadku tego pytania nie interesują mnie głębokie kopie kolekcji zmiennych typów, które mają swoje własne problemy.
(Żeby było jasne, przejrzałem kod źródłowy OpenJDK i mam jakąś odpowiedź na ArrayList
i TreeSet
.)
NavigableSet
inne Comparable
kolekcje oparte mogą czasem wykryć, czy klasa nie implementuje się compareTo()
poprawnie i zgłosić wyjątek. Nie jest jasne, co rozumiesz przez niezaufane argumenty. Masz na myśli, że złoczyńca tworzy kolekcję złych ciągów, a kiedy kopiujesz je do swojej kolekcji, dzieje się coś złego? Nie, struktura kolekcji jest dość solidna, istnieje od 1.2.
HashSet
(i wszystkich innych kolekcji mieszających ogólnie) zależy od poprawności / integralności hashCode
implementacji elementów TreeSet
i PriorityQueue
zależy od Comparator
(i nie możesz nawet utwórz równoważną kopię bez akceptowania niestandardowego komparatora, jeśli taki istnieje, EnumSet
ufa integralności określonego enum
typu, który nigdy nie jest weryfikowany po kompilacji, więc plik klasy, który nie został wygenerowany ani utworzony javac
ręcznie, może go obalić.
new TreeSet<>(strs)
gdzie strs
jest NavigableSet
. To nie jest kopia zbiorcza, ponieważ wynikowy TreeSet
użyje komparatora źródła, który jest nawet niezbędny do zachowania semantyki. Jeśli wszystko jest w porządku z przetwarzaniem zawartych elementów, toArray()
jest to dobra droga; zachowa nawet kolejność iteracji. Kiedy wszystko jest w porządku z „weź element, sprawdź poprawność elementu, użyj elementu”, nie musisz nawet robić kopii. Problemy zaczynają się, gdy chcesz zweryfikować wszystkie elementy, a następnie użyć wszystkich elementów. W takim razie nie można ufać TreeSet
kopiowaniu niestandardowego komparatora
checkcast
dla każdego elementu jest toArray
określony typ. Zawsze na tym kończymy. Kolekcje ogólne nawet nie znają faktycznego typu elementu, więc ich konstruktory kopii nie mogą zapewnić podobnej funkcjonalności. Oczywiście możesz odroczyć każdą kontrolę do właściwego wcześniejszego użycia, ale nie wiem, do czego zmierza twoje pytanie. Nie potrzebujesz „integralności semantycznej”, gdy dobrze Ci jest sprawdzanie i niepowodzenie bezpośrednio przed użyciem elementów.