Wie füge ich zwei Listen in Java zusammen?


Bedingungen: Ändern Sie nicht die ursprünglichen Listen; Nur JDK, keine externen Bibliotheken. Bonuspunkte für eine Einzeiler- oder eine JDK 1.3-Version.

Gibt es einen einfacheren Weg als:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);


Answers



Von der Spitze meines Kopfes kann ich es um eine Linie kürzen:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);



In Java 8:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
                             .collect(Collectors.toList());






Wahrscheinlich nicht einfacher, aber faszinierend und hässlich:

List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };

Verwenden Sie es nicht im Produktionscode ...;)




Eine Ihrer Anforderungen besteht darin, die ursprünglichen Listen beizubehalten. Wenn Sie eine neue Liste erstellen und addAll() , verdoppeln Sie effektiv die Anzahl der Verweise auf die Objekte in Ihren Listen. Dies kann zu Speicherproblemen führen, wenn Ihre Listen sehr groß sind.

Wenn Sie das verkettete Ergebnis nicht ändern müssen, können Sie dies mithilfe einer benutzerdefinierten Listenimplementierung vermeiden. Die benutzerdefinierte Implementierungsklasse ist offensichtlich mehr als eine Zeile ... aber sie ist kurz und süß.

CompositeUnmodifiableList.java:

public class CompositeUnmodifiableList<E> extends AbstractList<E> {

    private final List<E> list1;
    private final List<E> list2;

    public CompositeUnmodifiableList(List<E> list1, List<E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    @Override
    public E get(int index) {
        if (index < list1.size()) {
            return list1.get(index);
        }
        return list2.get(index-list1.size());
    }

    @Override
    public int size() {
        return list1.size() + list2.size();
    }
}

Verwendung:

List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);



Nicht einfacher, aber ohne die Größe zu ändern:

List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);



Fand diese Frage, um beliebig viele Listen zu verketten, ohne sich um externe Bibliotheken zu kümmern. Vielleicht wird es jemand anderem helfen:

com.google.common.collect.Iterables#concat()

Nützlich, wenn Sie dieselbe Logik auf mehrere verschiedene Sammlungen in einem for () anwenden möchten.




Ein weiterer Java 8 Einzeiler:

List<String> newList = Stream.of(listOne, listTwo)
                             .flatMap(x -> x.stream())
                             .collect(Collectors.toList());

Als Bonus können Sie Stream.of() viele Listen verketten, da Stream.of() variabel ist.

List<String> newList = Stream.of(listOne, listTwo, listThree)
                             .flatMap(x -> x.stream())
                             .collect(Collectors.toList());



Dies ist einfach und nur eine Zeile, fügt aber den Inhalt von listTwo zu listOne hinzu. Müssen Sie wirklich den Inhalt in eine dritte Liste setzen?

Collections.addAll(listOne, listTwo.toArray());



Etwas kürzer wäre:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);



Hier ist eine Java 8-Lösung mit zwei Zeilen:

List<Object> newList = new ArrayList<>();
Stream.of(list1, list2).forEach(newList::addAll);

Beachten Sie, dass diese Methode nicht verwendet werden sollte, wenn

  • Der Ursprung von newList ist nicht bekannt und es kann bereits mit anderen Threads geteilt werden
  • der Stream, der newList modifiziert, ist ein paralleler Stream und der Zugriff auf newList ist nicht synchronisiert oder threadsicher

aufgrund von Nebenwirkungen.

Beide der obigen Bedingungen gelten nicht für den obigen Fall des Zusammenfügens von zwei Listen, so dass dies sicher ist.

Basierend auf dieser Antwort auf eine andere Frage.




Etwas einfacher:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);



Sie können einen Oneliner ausführen, wenn die Zielliste vorbelegt ist.

(newList = new ArrayList<String>(list1)).addAll(list2);



Die vorgeschlagene Lösung ist für drei Listen, obwohl sie auch für zwei Listen angewendet werden kann. In Java 8 können wir Stream.of oder Stream.concat als:

List<String> result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream()).collect(Collectors.toList());
List<String> result2 = Stream.of(list1,list2,list3).flatMap(Collection::stream).collect(Collectors.toList());

Stream.concat zwei Streams als Eingabe und erstellt einen träge verketteten Stream, dessen Elemente alle Elemente des ersten Streams sind, gefolgt von allen Elementen des zweiten Streams. Da wir drei Listen haben, haben wir diese Methode ( Stream.concat ) zweimal verwendet.

Wir können auch eine Utility-Klasse (sagen wir StringUtils) mit einer Methode schreiben, die eine beliebige Anzahl von Listen (mit Varargs ) übernimmt und eine verkettete Liste zurückgibt als:

public static <T> List<T> concatenatedList(List<T>... collections) {
        return Arrays.stream(collections).flatMap(Collection::stream).collect(Collectors.toList()); 
}

Dann können wir diese Methode nutzen als:

List<String> result3 = StringUtils.concatenatedList(list1,list2,list3);



In Java 8 (andersherum):

List<?> newList = 
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());



eine weitere Liner-Lösung mit Java8 Stream, da flatMap bereits gebucht ist, hier eine Lösung ohne flatMap

List<E> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);

oder

List<E> ints = Stream.of(list1, list2).collect(ArrayList::new, List::addAll, List::addAll);

Code

    List<List<Integer>> lol = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
    List<Integer> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);
    System.out.println(lol);
    System.out.println(li);

Ausgabe

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]



Das klügste meiner Meinung nach:

/**
 * @param smallLists
 * @return one big list containing all elements of the small ones, in the same order.
 */
public static <E> List<E> concatenate (final List<E> ... smallLists)
{
    final ArrayList<E> bigList = new ArrayList<E>();
    for (final List<E> list: smallLists)
    {
        bigList.addAll(list);
    }
    return bigList;
}



Java 8-Version mit Unterstützung für das Verbinden mit einem Objektschlüssel:

public List<SomeClass> mergeLists(final List<SomeClass> left, final List<SomeClass> right, String primaryKey) {
    final Map<Object, SomeClass> mergedList = new LinkedHashMap<>();

    Stream.concat(left.stream(), right.stream())
        .map(someObject -> new Pair<Object, SomeClass>(someObject.getSomeKey(), someObject))
        .forEach(pair-> mergedList.put(pair.getKey(), pair.getValue()));

    return new ArrayList<>(mergedList.values());
}



Sie könnten es mit einem statischen Import und einer Helper-Klasse machen

nb die Generierung dieser Klasse könnte wahrscheinlich verbessert werden

public class Lists {

   private Lists() { } // can't be instantiated

   public static List<T> join(List<T>... lists) {
      List<T> result = new ArrayList<T>();
      for(List<T> list : lists) {
         result.addAll(list);
      }
      return results;
   }

}

Dann kannst du Dinge wie

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);



public static <T> List<T> merge(List<T>... args) {
    final List<T> result = new ArrayList<>();

    for (List<T> list : args) {
        result.addAll(list);
    }

    return result;
}



Verwenden Sie eine Helper-Klasse.

Ich schlage vor:

public static <E> Collection<E> addAll(Collection<E> dest, Collection<? extends E>... src) {
    for(Collection<? extends E> c : src) {
        dest.addAll(c);
    }

    return dest;
}

public static void main(String[] args) {
    System.out.println(addAll(new ArrayList<Object>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    // does not compile
    // System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList(4, 5, 6)));
}



Ich behaupte nicht, dass es einfach ist, aber du hast Bonus für Einzeiler erwähnt ;-)

Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
    new Vector(list1).elements(),
    new Vector(list2).elements(),
    ...
}))



Kein Weg in der Nähe von Einzeiler, aber ich denke, das ist das Einfachste:

List<String> newList = new ArrayList<String>(l1);
newList.addAll(l2);

for(String w:newList)
        System.out.printf("%s ", w);



Hier ist ein Ansatz mit Streams und Java 8, wenn Ihre Listen verschiedene Typen haben und Sie sie zu einer Liste eines anderen Typs kombinieren möchten.

public static void main(String[] args) {
    List<String> list2 = new ArrayList<>();
    List<Pair<Integer, String>> list1 = new ArrayList<>();

    list2.add("asd");
    list2.add("asdaf");
    list1.add(new Pair<>(1, "werwe"));
    list1.add(new Pair<>(2, "tyutyu"));

    Stream stream = Stream.concat(list1.stream(), list2.stream());

    List<Pair<Integer, String>> res = (List<Pair<Integer, String>>) stream
            .map(item -> {
                if (item instanceof String) {
                    return new Pair<>(0, item);
                }
                else {
                    return new Pair<>(((Pair<Integer, String>)item).getKey(), ((Pair<Integer, String>)item).getValue());
                }
            })
            .collect(Collectors.toList());
}



List<?> newList = ListUtils.combine(list1, list2);

Oh, du musst die combine Methode implementieren :-)




public class TestApp {

/**
 * @param args
 */
public static void main(String[] args) {
    System.out.println("Hi");
    Set<List<String>> bcOwnersList = new HashSet<List<String>>();
    List<String> bclist = new ArrayList<String>();
    List<String> bclist1 = new ArrayList<String>();
    List<String> object = new ArrayList<String>();
    object.add("BC11");
    object.add("C2");
    bclist.add("BC1");
    bclist.add("BC2");
    bclist.add("BC3");
    bclist.add("BC4");
    bclist.add("BC5");
    bcOwnersList.add(bclist);
    bcOwnersList.add(object);

    bclist1.add("BC11");
    bclist1.add("BC21");
    bclist1.add("BC31");
    bclist1.add("BC4");
    bclist1.add("BC5");

    List<String> listList= new ArrayList<String>();
    for(List<String> ll : bcOwnersList){
        listList = (List<String>) CollectionUtils.union(listList,CollectionUtils.intersection(ll, bclist1));
    }
    /*for(List<String> lists : listList){
        test = (List<String>) CollectionUtils.union(test, listList);
    }*/
    for(Object l : listList){
        System.out.println(l.toString());
    }
    System.out.println(bclist.contains("BC"));

}

}



Ich kann die Zweizeiler im allgemeinen Fall nicht verbessern, ohne Ihre eigene Hilfsmethode einzuführen, aber wenn Sie Listen von Strings haben und Sie sind bereit anzunehmen, dass diese Strings keine Kommas enthalten, können Sie diese lange ziehen. -liner:

List<String> newList = new ArrayList<String>(Arrays.asList((listOne.toString().subString(1, listOne.length() - 1) + ", " + listTwo.toString().subString(1, listTwo.length() - 1)).split(", ")));

Wenn Sie die Generics löschen, sollte dies JDK 1.4-kompatibel sein (obwohl ich das nicht getestet habe). Auch nicht für den Produktionscode empfohlen ;-)