wake-up-neo.net

So kopieren Sie die Java Collections-Liste

Ich habe ein ArrayList und ich möchte es genau kopieren. Ich verwende, wenn möglich, Dienstprogrammklassen unter der Annahme, dass jemand etwas Zeit damit verbracht hat, es richtig zu stellen. Also lande ich natürlich bei der Collections-Klasse, die eine Kopiermethode enthält.

Angenommen, ich habe Folgendes:

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

Dies schlägt fehl, weil im Grunde b nicht groß genug ist, um a aufzunehmen. Ja, ich weiß, dass b Größe 0 hat, aber es sollte groß genug sein, oder? Wenn ich b zuerst füllen muss, wird Collections.copy() eine völlig nutzlose Funktion in meinem Kopf. Gibt es also einen geeigneten Weg, um eine Kopierfunktion zu programmieren (was ich jetzt tun werde)?

128
Jasper Floor

Berufung

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

erstellt eine flache Kopie von a innerhalb von b. Alle Elemente werden innerhalb von b in genau derselben Reihenfolge vorhanden sein, in der sie sich innerhalb von a befanden (vorausgesetzt es hatte eine Reihenfolge).

Ebenso anrufen

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

erstellt auch eine flache Kopie von a innerhalb von b. Wenn der erste Parameter b nicht über genügend capacity (nicht size) verfügt, um alle Elemente der a zu enthalten, wird eine IndexOutOfBoundsException ausgegeben. Die Erwartung ist, dass Collections.copy keine Zuordnungen erforderlich ist, und wenn überhaupt, wird diese Ausnahme ausgelöst. Es ist eine Optimierung, um die Vorbelegung der kopierten Sammlung (b) zu verlangen, aber ich denke im Allgemeinen nicht, dass sich das Feature aufgrund der erforderlichen Überprüfungen lohnt, wenn man die Konstruktor-basierten Alternativen wie die oben gezeigte verwendet, die keine ungewöhnlichen Nebenwirkungen haben.

Um eine tiefe Kopie zu erstellen, müsste die Variable List über einen der beiden Mechanismen komplizierte Kenntnisse des zugrunde liegenden Typs besitzen. Im Fall von Strings, die in Java (und in dieser Hinsicht auch in .NET) unveränderlich sind, benötigen Sie nicht einmal eine tiefe Kopie. Im Fall von MySpecialObject müssen Sie wissen, wie eine tiefe Kopie davon erstellt wird. Dies ist keine generische Operation.


Hinweis: Die ursprünglich akzeptierte Antwort war das beste Ergebnis für Collections.copy bei Google und war, wie in den Kommentaren ausgeführt, völlig falsch.

109
Stephen Katulka

b hat eine Kapazität von 3, aber eine size von 0. Die Tatsache, dass ArrayList eine Art Pufferkapazität hat, ist ein Implementierungsdetail - es ist nicht Teil der List-Schnittstelle, daher wird Collections.copy(List, List) nicht verwendet es. Es wäre hässlich für den Sonderfall ArrayList.

Wie MrWiggles angedeutet hat, ist die Verwendung des ArrayList-Konstruktors, der eine Auflistung verwendet, im angegebenen Beispiel der richtige Weg.

Für kompliziertere Szenarien (die möglicherweise Ihren tatsächlichen Code enthalten können) kann die Google Java Collections-Bibliothek nützlich sein.

130
Jon Skeet

Mach einfach:

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

ArrayList verfügt über einen Konstruktor, der eine andere Collection akzeptiert, aus der die Elemente kopiert werden

58
tddmonkey

Die Antwort von Stephen Katulka (akzeptierte Antwort) ist falsch (der zweite Teil) ..__ erklärt, dass Collections.copy(b, a); eine tiefe Kopie macht, was nicht der Fall ist. Sowohl new ArrayList(a); als auch Collections.copy(b, a); führen nur eine flache Kopie aus. Der Unterschied besteht darin, dass der Konstruktor neuen Speicher zuweist, und copy(...) dies nicht tut. Dies macht ihn für Fälle geeignet, in denen Sie Arrays wiederverwenden können, da sie einen Leistungsvorteil bieten.

Die Java-Standard-API versucht, die Verwendung von tiefen Kopien zu unterbinden, da es schlecht wäre, wenn neue Codierer diese Funktion regelmäßig verwenden würden. Dies könnte auch einer der Gründe dafür sein, dass clone() standardmäßig nicht öffentlich ist.

Den Quellcode für Collections.copy(...) finden Sie in Zeile 552 unter: http://www.Java2s.com/Open-Source/Java-Document/6.0-JDK-Core/Collections-Jar-Zip-Logging- Regex/Java/util/Collections.Java.htm

Wenn Sie eine tiefe Kopie benötigen, müssen Sie die Elemente manuell durchlaufen, indem Sie für jedes Objekt eine for-Schleife und einen Klon () verwenden.

16
hoijui

die einfachste Möglichkeit, eine Liste zu kopieren, besteht darin, sie an den Konstruktor der neuen Liste zu übergeben:

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

b wird eine flache Kopie von a sein

Bei der Suche nach der Quelle von Collections.copy(List,List) (hatte ich es noch nie zuvor gesehen) scheint es für das Kopieren des Elementindex nach Index zu sein. Wenn Sie List.set(int,E) verwenden, wird das Element 0 das Element 0 in die Zielliste usw. überschreiben. Nicht besonders deutlich aus den Javadocs, die ich zugeben muss.

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'
12
Gareth Davis
List b = new ArrayList(a.size())

legt die Größe nicht fest Es legt die anfängliche Kapazität fest (dh wie viele Elemente passen, bevor die Größe geändert werden muss). Eine einfachere Art des Kopierens ist in diesem Fall:

List b = new ArrayList(a);
9
cletus

Wie hoijui erwähnt. Die ausgewählte Antwort von Stephen Katulka enthält einen falschen Kommentar zu Collections.copy. Der Autor akzeptierte es wahrscheinlich, weil die erste Codezeile die gewünschte Kopie erstellte. Der zusätzliche Aufruf von Collections.copy wird nur noch einmal kopiert. (Die Kopie wird zweimal ausgeführt.).

Hier ist der Code, um es zu beweisen.

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
Michael Welch

Warum verwenden Sie nicht einfach die addAll-Methode:

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

    List b = b.addAll(listA);

//b will be 1, abc

auch wenn Sie bereits Elemente in b haben oder Sie einige Elemente danach anhängen möchten, z.

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
Vin.X

Die meisten Antworten hier erkennen das Problem nicht, der Benutzer möchte eine KOPIE der Elemente von der ersten Liste in die zweite Liste, Ziellistenelemente sind neue Objekte und beziehen sich nicht auf die Elemente der ursprünglichen Liste Das Element der zweiten Liste sollte die Werte für das entsprechende Element der Quellenliste nicht ändern.) Für die veränderlichen Objekte können wir den ArrayList (Collection) -Konstruktor nicht verwenden, da er einfach auf das ursprüngliche Listenelement verweist und .... nicht kopiert Sie müssen für jedes Objekt einen Listen-Kloner haben, wenn Sie kopieren.

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

Wenn Sie eine ArrayList kopieren möchten, kopieren Sie sie mit:

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

List a = new ArrayList(b);
3
Martin C.

Strings können tief kopiert werden

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

weil sie unveränderlich sind. Jedes andere Objekt nicht -> Sie müssen iterieren und eine Kopie selbst erstellen.

2
felix

Die folgende Ausgabe veranschaulicht die Ergebnisse der Verwendung von copy constructor und 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)

Die Quelle des vollständigen Programms ist hier: Java List-Kopie . Aber die Ausgabe reicht aus, um zu sehen, wie sich Java.util.Collections.copy () verhält.

1
pwojnowski

Jedes andere Objekt nicht -> Sie müssen iterieren und eine Kopie selbst erstellen.

Um dies zu vermeiden, implementieren Sie 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);               
        }
       */

  }
1
Juan Castillo

Und wenn Sie Google Guava verwenden, wäre dies die einzeilige Lösung 

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

Dadurch wird eine Instanz einer veränderlichen Array-Liste erstellt.

0
vsingh

Da Java 8 null-sicher ist, können Sie den folgenden Code verwenden.

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

Oder mit einem Sammler

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

Kopieren ist nicht sinnlos, wenn Sie sich den Anwendungsfall vorstellen, einige Werte in eine vorhandene Sammlung zu kopieren. Das heißt Sie möchten vorhandene Elemente überschreiben, anstatt sie einzufügen.

Ein Beispiel: a = [1,2,3,4,5] b = [2,2,2,2,3,3,3,3,3,4,4,4] a.copy (b) = [1,2,3,4,5,3,3,3,3,4,4,4]

Ich würde jedoch eine Kopiermethode erwarten, die zusätzliche Parameter für den Startindex der Quell- und Zielsammlung sowie einen Parameter für die Zählung erfordern würde.

Siehe Java BUG 6350752

0
ordnungswidrig