Как скопировать список коллекций Java - программирование

Как скопировать список коллекций Java

У меня есть ArrayList, и я хочу его скопировать. Я использую классы полезности, когда это возможно, исходя из предположения, что кто-то потратил некоторое время на исправление. Поэтому, естественно, я заканчиваю с классом Collections, который содержит метод копирования.

Предположим, что у меня есть следующее:

List<String> a = new ArrayList<String>();
a.add("a");
a.add("b");
a.add("c");
List<String> b = new ArrayList<String>(a.size());

Collections.copy(b,a);

Это не удается, потому что в основном он считает, что b недостаточно велик, чтобы удерживать a. Да, я знаю, что b имеет размер 0, но он должен быть достаточно большим, не так ли? Если мне сначала нужно заполнить b, тогда Collections.copy() станет совершенно бесполезной функцией в моем сознании. Итак, кроме программирования функции копирования (которую я сейчас буду делать), есть ли способ сделать это?

4b9b3361

Ответ 1

Вызов

List<String> b = new ArrayList<String>(a);

создает мелкую копию a внутри b. Все элементы будут существовать в пределах b в том же порядке, что они были в пределах a (при условии, что у него был порядок).

Аналогично, вызов

// note: instantiating with a.size() gives `b` enough capacity to hold everything
List<String> b = new ArrayList<String>(a.size());
Collections.copy(b, a);

также создает неглубокую копию a внутри b. Если первый параметр b не имеет достаточной емкости (не размер), чтобы содержать все элементы a, тогда он выдает IndexOutOfBoundsException. Ожидается, что никакие ассигнования не потребуются Collections.copy для работы, а если они есть, то это исключает это исключение. Оптимизация требует, чтобы скопированная коллекция была предварительно распределена (b), но я вообще не думаю, что эта функция стоит того, что было сделано с помощью необходимых проверок, учитывая альтернативы на основе конструктора, такие как приведенные выше, которые не имеют странной стороны эффекты.

Чтобы создать глубокую копию, List через любой из этих механизмов должен иметь сложное знание базового типа. В случае String s, которые неизменны в Java (и .NET, если на то пошло), вам даже не нужна глубокая копия. В случае MySpecialObject вам нужно знать, как сделать глубокую копию, и это не общая операция.


Примечание. Первоначально принятый ответ был лучшим результатом для Collections.copy в Google, и он был неверным, как указано в комментариях.

Ответ 2

b имеет емкость 3, но размер 0. Тот факт, что ArrayList имеет какую-то емкость буфера, является деталью реализации - он не является частью интерфейса List, поэтому Collections.copy(List, List) не использует его., Это было бы уродливо для особого случая ArrayList.

Как указал MrWiggles, в представленном примере используется способ конструктора ArrayList, который принимает коллекцию.

Для более сложных сценариев (которые могут включать в себя ваш реальный код) вы можете найти полезные коллекции в Guava.

Ответ 3

Просто выполните:

List a = new ArrayList(); 
a.add("a"); 
a.add("b"); 
a.add("c"); 
List b = new ArrayList(a);

ArrayList имеет конструктор, который будет принимать другую коллекцию для копирования элементов из

Ответ 4

Ответ Стивена Катулки (принятый ответ) неверен (вторая часть). Это объясняет, что Collections.copy(b, a); делает глубокую копию, а это не так. Оба, new ArrayList(a); и Collections.copy(b, a); выполняют только мелкую копию. Разница в том, что конструктор выделяет новую память, а copy(...) - нет, что делает ее подходящей в случаях, когда вы можете повторно использовать массивы, так как она имеет преимущество в производительности.

Стандартный API Java пытается отказать в использовании глубоких копий, поскольку было бы плохо, если бы новые кодеры использовали это на регулярной основе, что также может быть одной из причин, по которым clone() не является общедоступным по умолчанию.

Исходный код для Collections.copy(...) можно увидеть в строке 552 по адресу: http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging-regex/java/util/Collections.java.htm

Если вам нужна глубокая копия, вам нужно перебирать элементы вручную, используя для цикла и клона() для каждого объекта.

Ответ 5

Самый простой способ скопировать список - передать его конструктору нового списка:

List<String> b = new ArrayList<>(a);

b будет мелкой копией a

Глядя на источник Collections.copy(List,List) (я никогда не видел его раньше), похоже, для копирования индекса элементов по индексу. используя List.set(int,E), таким образом, элемент 0 будет переписывать элемент 0 в списке целей и т.д. и т.д. Не особенно ясно из javadocs, которые я должен был бы признать.

List<String> a = new ArrayList<>(a);
a.add("foo");
b.add("bar");

List<String> b = new ArrayList<>(a); // shallow copy 'a'

// the following will all hold
assert a.get(0) == b.get(0);
assert a.get(1) == b.get(1);
assert a.equals(b);
assert a != b; // 'a' is not the same object as 'b'

Ответ 6

List b = new ArrayList(a.size())

не задает размер. Он устанавливает начальную емкость (количество элементов, которые она может вместить, прежде чем потребуется изменить размер). Более простой способ копирования в этом случае:

List b = new ArrayList(a);

Ответ 7

Как упоминает hoijui. Выбранный ответ от Stephen Katulka содержит комментарий о Collections.copy, который является неправильным. Вероятно, автор воспринял это, потому что первая строка кода делала копию, которую он хотел. Дополнительный вызов Collections.copy просто копирует снова. (Результирующая копия происходит дважды).

Вот код, чтобы доказать это.

public static void main(String[] args) {

    List<String> a = new ArrayList<String>();
    a.add("a");
    a.add("b");
    a.add("c");
    List<String> b = new ArrayList<String>(a);

    System.out.println("There should be no output after this line.");

    // Note, b is already a shallow copy of a;
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) != b.get(i)) {
            System.out.println("Oops, this was a deep copy."); // Note this is never called.
        }
    }

    // Now use Collections.copy and note that b is still just a shallow copy of a
    Collections.copy(b, a);
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) != b.get(i)) {
            System.out.println("Oops, i was wrong this was a deep copy"); // Note this is never called.
        }
    }

    // Now do a deep copy - requires you to explicitly copy each element
    for (int i = 0; i < a.size(); i++) {
        b.set(i, new String(a.get(i)));
    }

    // Now see that the elements are different in each 
    for (int i = 0; i < a.size(); i++) {
        if (a.get(i) == b.get(i)) {
            System.out.println("oops, i was wrong, a shallow copy was done."); // note this is never called.
        }
    }
}

Ответ 8

Большинство ответов здесь не осознают проблему, пользователь хочет иметь КОПИЮ элементов из первого списка во второй список, элементы списка назначения - это новые объекты, а не ссылки на элементы исходного списка. (означает, что изменение элемента второго списка не должно изменять значения для соответствующего элемента списка источников.) Для изменяемых объектов мы не можем использовать конструктор ArrayList (Collection), потому что он будет просто ссылаться на исходный элемент списка и не будет копироваться. При копировании необходимо иметь клонирование списка для каждого объекта.

Ответ 9

Почему вы просто не используете метод addAll:

    List a = new ArrayList();
         a.add("1");
         a.add("abc");

    List b = b.addAll(listA);

//b will be 1, abc

даже если у вас есть существующие элементы в b или вы хотите отложить некоторые элементы после него, например:

List a = new ArrayList();
     a.add("1");
     a.add("abc");

List b = new ArrayList();
     b.add("x");
     b.addAll(listA);
     b.add("Y");

//b will be x, 1, abc, Y

Ответ 10

Если вы хотите скопировать ArrayList, скопируйте его, используя:

List b = new ArrayList();
b.add("aa");
b.add("bb");

List a = new ArrayList(b);

Ответ 11

Строки могут быть скопированы с помощью

List<String> b = new ArrayList<String>(a);

потому что они неизменяемы. Каждый другой объект не → вам нужно выполнить итерацию и сделать копию самостоятельно.

Ответ 12

private List<Item> cloneItemList(final List<Item> items)
    {
        Item[] itemArray = new Item[items.size()];
        itemArray = items.toArray(itemArray);
        return Arrays.asList(itemArray);
    }

Ответ 13

Каждый другой объект не → вам нужно выполнить итерацию и сделать копию самостоятельно.

Чтобы избежать этого применения Cloneable.

public class User implements Serializable, Cloneable {

    private static final long serialVersionUID = 1L;

    private String user;
    private String password;
    ...

    @Override
    public Object clone() {
        Object o = null;
        try {
          o = super.clone();
        } catch(CloneNotSupportedException e) {
        }
        return o;
     }
 }

....

  public static void main(String[] args) {

      List<User> userList1 = new ArrayList<User>();

      User user1 = new User();
      user1.setUser("User1");
      user1.setPassword("pass1");
      ...

      User user2 = new User();
      user2.setUser("User2");
      user2.setPassword("pass2");
      ...

      userList1 .add(user1);
      userList1 .add(user2);

      List<User> userList2 = new ArrayList<User>();


      for(User u: userList1){
          u.add((User)u.clone());
      }

      //With this you can avoid 
      /*
        for(User u: userList1){
            User tmp = new User();
            tmp.setUser(u.getUser);
            tmp.setPassword(u.getPassword);
            ...
            u.add(tmp);               
        }
       */

  }

Ответ 14

Следующий результат иллюстрирует результаты использования конструктора копирования и Collections.copy():

Copy [1, 2, 3] to [1, 2, 3] using copy constructor.

Copy [1, 2, 3] to (smaller) [4, 5]
java.lang.IndexOutOfBoundsException: Source does not fit in dest
        at java.util.Collections.copy(Collections.java:556)
        at com.farenda.java.CollectionsCopy.copySourceToSmallerDest(CollectionsCopy.java:36)
        at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:14)

Copy [1, 2] to (same size) [3, 4]
source: [1, 2]
destination: [1, 2]

Copy [1, 2] to (bigger) [3, 4, 5]
source: [1, 2]
destination: [1, 2, 5]

Copy [1, 2] to (unmodifiable) [4, 5]
java.lang.UnsupportedOperationException
        at java.util.Collections$UnmodifiableList.set(Collections.java:1311)
        at java.util.Collections.copy(Collections.java:561)
        at com.farenda.java.CollectionsCopy.copyToUnmodifiableDest(CollectionsCopy.java:68)
        at com.farenda.java.CollectionsCopy.main(CollectionsCopy.java:20)

Источник полной программы находится здесь: копия списка Java. Но выход достаточно, чтобы увидеть, как ведет себя java.util.Collections.copy().

Ответ 15

Если Java 8 является нулевым, вы можете использовать следующий код.

List<String> b = Optional.ofNullable(a)
                         .map(list -> (List<String>) new ArrayList<>(list))
                         .orElseGet(Collections::emptyList);

Или используя коллекционер

List<String> b = Optional.ofNullable(a)
                         .map(List::stream)
                         .orElseGet(Stream::empty)
                         .collect(Collectors.toList())

Ответ 16

Копирование не бесполезно, если вы предположите, что прецедент используется для копирования некоторых значений в существующую коллекцию. То есть вы хотите перезаписать существующие элементы вместо вставки.

Пример: a = [1,2,3,4,5] b = [2,2,2,2,3,3,3,3,3,4,4,4,4,] a.copy(b) = [1,2,3,4,5,3,3,3,3,4,4,4]

Однако я бы ожидал, что метод копирования будет принимать дополнительные параметры для начального индекса исходной и целевой коллекции, а также параметр для count.

См. Java BUG 6350752

Ответ 17

И если вы используете google guava, однострочное решение будет

List<String> b = Lists.newArrayList(a);

Это создает экземпляр списка изменяемых массивов.

Ответ 18

Чтобы понять, почему Collections.copy() выдает исключение IndexOutOfBoundsException, хотя вы сделали массив поддержки из списка адресатов достаточно большим (через вызов size() в sourceList), см. ответ Абхая Ядава в этом связанном вопросе: Как скопировать java.util.List в другой java.util.List