Как скопировать список коллекций Java - PullRequest
135 голосов
/ 27 марта 2009

У меня есть 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() становится совершенно бесполезной функцией в моем уме. Итак, кроме программирования функции копирования (которую я собираюсь сделать сейчас), есть ли правильный способ сделать это?

Ответы [ 18 ]

131 голосов
/ 27 марта 2009

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

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

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

110 голосов
/ 06 ноября 2009

Calling

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, и он был неверным, как указано в комментариях.

58 голосов
/ 27 марта 2009

Просто сделай:

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

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

16 голосов
/ 24 февраля 2011

Ответ Стивена Катульки (принятый ответ) неправильный (вторая часть). Это объясняет, что 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

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

12 голосов
/ 27 марта 2009

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

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'
9 голосов
/ 27 марта 2009
List b = new ArrayList(a.size())

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

List b = new ArrayList(a);
8 голосов
/ 07 апреля 2011

Как упоминает hoijui. Выбранный ответ Стивена Катульки содержит неверный комментарий о 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.
        }
    }
}
5 голосов
/ 01 мая 2013

Почему вы просто не используете 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
5 голосов
/ 10 октября 2011

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

3 голосов
/ 27 июля 2016
private List<Item> cloneItemList(final List<Item> items)
    {
        Item[] itemArray = new Item[items.size()];
        itemArray = items.toArray(itemArray);
        return Arrays.asList(itemArray);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...