Копіювальні набори Java


83

Чи є спосіб скопіювати TreeSet? Тобто, чи можна їхати

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

tempList = itemList;

чи вам доводиться фізично переглядати набори та копіювати їх по одному?


7
tempList.addAll(itemList)
dhblah

Відповіді:


156

Інший спосіб зробити це - використовувати конструктор копіювання :

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

Або створіть порожній набір і додайте елементи:

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

На відміну від cloneних, ви можете використовувати інший набір класів, інший порівняльник або навіть заповнювати з іншого (невстановленого) типу колекції.


Зверніть увагу, що результатом копіювання a Setє нове, Setщо містить посилання на об'єкти, які є елементами, якщо оригінал Set. Самі об'єкти елементів не копіюються і не клонуються. Це відповідає тому, як CollectionAPI Java розроблені для роботи: вони не копіюють об'єкти елементів.


7

За допомогою Java 8 ви можете використовувати streamі collectкопіювати елементи:

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

Або ви можете отримати до ImmutableSet(якщо ви знаєте, що набір не повинен змінюватися):

Set<Item> newSet = oldSet.stream().collect(ImmutableSet.toImmutableSet());

8
Ви можете ... але конструктор копіювання (і т.д.) повинен бути більш ефективним, якщо ви просто копіюєте колекцію.
Stephen C

3

Конструктор копіювання, наданий @Stephen C, - це той шлях, який Setви створили (або коли знаєте, звідки він береться). Коли воно походить від a Map.entrySet(), це буде залежати від Mapреалізації, яку ви використовуєте:

findbugs каже

Методу entrySet () дозволено повертати подання базової карти, в якій один об'єкт Entry використовується повторно і повертається під час ітерації. Починаючи з Java 1.6, це робили і IdentityHashMap, і EnumMap. Під час ітерації через таку карту значення Entry є дійсним лише до переходу до наступної ітерації. Наприклад, якщо ви спробуєте передати такий запис SetSet до методу addAll, все піде не так.

Як addAll()називається конструктор копіювання, ви можете опинитися з набором лише одного запису: останнього.

MapХоча це роблять не всі реалізації, тому, якщо ви знаєте, що ваша реалізація безпечна в цьому відношенні, конструктор копіювання, безумовно, є правильним шляхом. В іншому випадку вам доведеться створювати нові Entryоб'єкти самостійно:

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));

Редагувати: На відміну від тестів, які я проводив на Java 7 та Java 6u45 (завдяки Степану С), коментар findbugs вже не здається доречним. Це могло бути у попередніх версіях Java 6 (до u45), але у мене немає жодної для тестування.


1
Це засновано на спостереженні? Якщо так, це звучить як помилка у addAllреалізації. FWIW, Mapреалізації, які я переглядав, всі повторюють набір записів (на якомусь рівні) і витягують ключ і значення для кожного з них . Той факт, що ітератор набору записів може повертати той самий об'єкт кожного разу, не має значення. Єдиний випадок, який я бачив, був інший, EnumMapколи конструктор копіювання клонував записи ... якщо вихідна карта була EnumMap.
Stephen C

1
@StephenC, здається, ти маєш рацію: тести, якими я IdentityHashMapзаймався, не призводять до цієї помилки. Більше турбує те, що я тестував його на Java 6u45 і теж не було проблем. Я думаю, це помилка у findbugs (або JDK, на якому вони базували свої правила ...). Я відредагую свою відповідь.
Матьє

3

Починаючи з Java 10 :

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

Set.copyOf()повертає немодифікований, Setщо містить елементи заданого Collection.

Дане Collectionне повинно бути null, і воно не повинно містити жодних nullелементів.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.