Kopiowanie ustawia Java

83

Czy istnieje sposób na skopiowanie TreeSet? To znaczy, czy można iść

Set <Item> itemList;
Set <Item> tempList;

tempList = itemList;

czy też musisz fizycznie iterować zestawy i kopiować je jeden po drugim?

SNpn
źródło
8
tempList.addAll(itemList)
dhblah

Odpowiedzi:

156

Innym sposobem na to jest użycie konstruktora kopiującego :

Collection<E> oldSet = ...
TreeSet<E> newSet = new TreeSet<E>(oldSet);

Lub stwórz pusty zestaw i dodaj elementy:

Collection<E> oldSet = ...
TreeSet<E> newSet = new TreeSet<E>();
newSet.addAll(oldSet);

W przeciwieństwie do clonenich pozwalają na użycie innej klasy zestawu, innego komparatora, a nawet wypełnienie z innego (niezestawionego) typu kolekcji.


Zauważ, że wynikiem kopiowania a Setjest nowy Setzawierający odniesienia do obiektów, które są elementami oryginału Set. Same obiekty elementów nie są kopiowane ani klonowane. Jest to zgodne ze sposobem działania Collectioninterfejsów API języka Java : nie kopiują one obiektów elementów.

Stephen C.
źródło
7

W Javie 8 możesz używać streami collectkopiować elementy:

Set<Item> newSet = oldSet.stream().collect(Collectors.toSet());

Lub możesz zebrać do ImmutableSet(jeśli wiesz, że zestaw nie powinien się zmieniać):

Set<Item> newSet = oldSet.stream().collect(ImmutableSet.toImmutableSet());
Yosi Dahari
źródło
8
Możesz ... ale konstruktor kopiujący (itp.) Powinien być bardziej wydajny, jeśli po prostu kopiujesz kolekcję.
Stephen C,
3

Konstruktor kopiujący podany przez @Stephen C jest drogą do zrobienia, gdy Setmasz utworzony przez siebie (lub gdy wiesz, skąd pochodzi). Jeśli pochodzi z a Map.entrySet(), będzie to zależeć od używanej Mapimplementacji:

Findbugs mówi

Metoda entrySet () może zwrócić widok bazowej mapy, w której pojedynczy obiekt Entry jest ponownie używany i zwracany podczas iteracji. Począwszy od Javy 1.6, zrobiły to zarówno IdentityHashMap, jak i EnumMap. Podczas iteracji przez taką mapę wartość Entry jest ważna tylko do momentu przejścia do następnej iteracji. Jeśli na przykład spróbujesz przekazać taki entrySet do metody addAll, wszystko pójdzie źle.

Jak addAll()nazywa konstruktor kopiujący, możesz znaleźć zestaw zawierający tylko jeden wpis: ostatni.

MapJednak nie wszystkie implementacje to robią, więc jeśli wiesz, że Twoja implementacja jest bezpieczna pod tym względem, zdecydowanie najlepszym rozwiązaniem jest konstruktor kopiujący. W przeciwnym razie sam musiałbyś tworzyć nowe Entryobiekty:

Set<K,V> copy = new HashSet<K,V>(map.size());
for (Entry<K,V> e : map.entrySet())
    copy.add(new java.util.AbstractMap.SimpleEntry<K,V>(e));

Edycja: W przeciwieństwie do testów, które przeprowadziłem na Javie 7 i Javie 6u45 (dzięki Stephenowi C), komentarz findbugs nie wydaje się już odpowiedni. Mogło tak być we wcześniejszych wersjach Java 6 (przed U45), ale nie mam żadnej do testowania.

Matthieu
źródło
1
Czy jest to oparte na obserwacji? Jeśli tak, to brzmi jak błąd w addAllimplementacji. FWIW, wszystkie Mapimplementacje , których szukałem, iterują zestaw wpisów (na pewnym poziomie) i wyodrębniają klucz i wartość dla każdego z nich . Fakt, że iterator zestawu pozycji może zwracać ten sam obiekt za każdym razem, nie ma znaczenia. Jedyny przypadek, w którym zauważyłem, że był inny, to EnumMapsytuacja, w której sam konstruktor kopiujący klonował wpisy ... jeśli mapa źródłowa była EnumMap.
Stephen C
1
@StephenC wygląda na to, że masz rację: testy, które wykonałem IdentityHashMap, nie prowadzą do tego błędu. Bardziej niepokojące jest to, że przetestowałem go na Javie 6u45 i też nie było problemu. Wydaje mi się, że jest to błąd w findbugs (lub JDK, na którym oparli swoje zasady ...). Zmienię odpowiedź.
Matthieu,
3

Począwszy od Java 10 :

Set<E> oldSet = Set.of();
Set<E> newSet = Set.copyOf(oldSet);

Set.copyOf()zwraca niemodyfikowalny Setzawierający elementy danego Collection.

Podane Collectionnie może być nulli nie może zawierać żadnych nullelementów.

Oleksandr Pyrohov
źródło
2

Java 8+ :

Set<String> copy = new HashSet<>(mySet); 
J. Doe
źródło