Jak dołączyć do dwóch list w Javie?

749

Warunki: nie modyfikuj oryginalnych list; Tylko JDK, brak bibliotek zewnętrznych. Punkty bonusowe za wersję jedno-liniową lub wersję JDK 1.3.

Czy istnieje prostszy sposób niż:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
Robert Atkins
źródło
5
Jeśli robisz to wyłącznie w celu iteracji, zobacz inne pytanie - są Google Guava i rozwiązania Java 8 stackoverflow.com/questions/4896662/...
Boris Treukhov
Rozwiązanie Java 8 z metodą narzędziową: stackoverflow.com/a/37386846/1216775
akhil_mittal
Po przeczytaniu niektórych odpowiedzi przepraszam, że zapytałem.
Anthony Rutledge

Odpowiedzi:

590

W Javie 8:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
                             .collect(Collectors.toList());
Dale Emery
źródło
82
Gawd, to jest coś w Javie 8? Wydaje mi się, że technicznie wygrywasz, ale to długa kolejka :-)
Robert Atkins
4
Dla zwykłego czytelnika, oto krótsze rozwiązanie wykorzystujące również Java _ Streams: stackoverflow.com/a/34090554/363573
Stephan
7
Jest brzydki, ale przynajmniej płynnie i można go stosować bez lambda wieloliniowego. Naprawdę żałuję, że nie było płynnego dodawania wszystkiego, co zwróciło listę konkatyfikowaną.
Usman Ismail
6
Wydaje mi się, że warto zauważyć, że naprawdę łatwo jest uzyskać z tego wyraźną listę, taką jak:List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
Roger
1
Alternatywa dla konkat: strumień strumieniStream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
Peter Walser
569

Z czubka głowy mogę skrócić go o jedną linię:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
AdamC
źródło
156
Chociaż technicznie masz rację, skróciłeś go o jedną linię, asymetria tego mnie wkurza. Wystarczy, że chętniej „wydam” dodatkową linię.
Robert Atkins,
13
Czy nie ma tu problemu, że tablica interali newList zostanie zainicjalizowana do rozmiaru listOne, a następnie będzie musiała potencjalnie rozwinąć się podczas dodawania wszystkich elementów z listTwo? Czy lepiej byłoby wziąć rozmiar każdej listy i użyć jej do zmiany rozmiaru nowej tablicy?
Eric
2
To było najlepsze rozwiązanie dla mnie. Dokonałem porównania wydajności różnych rozwiązań, które wyłoniły zwycięzcę, wraz z utworzeniem pustej listy, a następnie addAll()obu. Próbowałem wszystkich tych, które sugerują, że nie będę kopiować list, a one skutkują dużym obciążeniem, którego nie potrzebowaliśmy tym razem.
manuelvigarcia
fajnie, ale można nawet skrócić: List list = new ArrayList <> (list1) .addAll (list2);
prędkość
1
@velocity: nie, to nie zadziała. addAll(Collection)zwraca a boolean.
Stijn Van Bael
306

Możesz użyć biblioteki wspólnych kolekcji Apache :

List<String> newList = ListUtils.union(list1, list2);
Guillermo
źródło
52
Fajnie, ale wymaga wspólnego apache.
Określił
101
@ Quantum7, nadal przydatne dla innych osób;) Czy apache commons jest nawet biblioteką zewnętrzną? Bez tego niczego nie zaczynam!
tster
28
@Platinum Nie, zgodnie z dokumentacją ListUtils.union jest dokładnie równoważne kodowi OP. Ale być może wprowadzanie w błąd operacji SET („Union”) w kontekście listy jest mylące. Widzę, jak możesz oczekiwać, że usunie duplikaty lub coś, ale wygląda na to, że metoda tego nie robi.
Quantum7
24
Unikaj kolekcji Apache Commons. Nie jest bezpieczny, nie ma żadnych leków generycznych. Świetnie, jeśli używasz Java 1.4, ale dla Java 5 i wyższych wolałbym Google Guava.
Michael Piefel,
11
@MichaelPiefel Najnowsze Apache Commons Collections 4 są bezpieczne pod względem typu. Dzięki referencjom do metody Java 8 tego rodzaju statyczne narzędzia stają się bardzo ważne.
mingfai
93

Jednym z twoich wymagań jest zachowanie oryginalnych list. Jeśli utworzysz nową listę i użyjesz addAll(), skutecznie podwajasz liczbę odwołań do obiektów na twoich listach. Może to prowadzić do problemów z pamięcią, jeśli twoje listy są bardzo duże.

Jeśli nie musisz modyfikować połączonego wyniku, możesz tego uniknąć za pomocą niestandardowej implementacji listy. Niestandardowa klasa implementacji to oczywiście więcej niż jedna linia ... ale korzystanie z niej jest krótkie i słodkie.

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

Stosowanie:

List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);
Kevin K.
źródło
15
To jest prawdziwa odpowiedź na to pytanie.
Wouter Lievens
9
Jest to wykonalne rozwiązanie, ale należy pamiętać, że jeśli podstawowe obiekty listy zmienią się (lista1, lista2), zawartość tej listy ulegnie zmianie. Możesz nie być w stanie zmodyfikować samej instancji CompositeUnmodifiableList , ale jeśli możesz uzyskać odwołanie do oryginalnych list, możesz to zrobić. Także dla tych, którzy nie są zaznajomieni: ostateczny modyfikator wpływa tylko na odwołanie do samego obiektu listy, nie może się zmienić, ale zawartość listy może się zmienić!
jwj
3
@jwj wszystkie bardzo dobre punkty, dziękuję. Nazwa klasy prawdopodobnie zasługuje na wyjaśnienie. Widzę tę klasę jako coś bardzo podobnego do Collections.unmodifiableList()metody, która owija listę, aby uczynić ją niemodyfikowalną. CompositeUnmodifiableListrobi to samo, tyle że zawija dwie listy i zapewnia skonkatenowany widok. Wszystkie punkty, o CompositeUnmodifiableListktórych mówisz, są również prawdziwe Collections.unmodifiableList().
Kevin K,
2
Konstruktor może wziąćList<? extends E>
Patrick Parker
84

Prawdopodobnie nie prostsze, ale intrygujące i brzydkie:

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

Nie używaj go w kodzie produkcyjnym ...;)

salwa
źródło
44
Brzydkie i złe, tak jak prawie każde użycie inicjalizacji podwójnego nawiasu klamrowego. Jest jednak krótszy;)
Jorn
4
@MarnixKlooster: Eclipse wie , że nie należy go używać i sprawia, że ​​korzystanie z niego jest nieprzyjemne ;-)
Joachim Sauer
20
Chociaż fizycznie jest to jedna linia, nie uważam tego za „jedną linię”.
splungebob,
11
dlaczego ludzie nienawidzą anonimowych inicjatorów bloków
NimChimpsky
18
@NimChimpsky Myślę, że to głównie dlatego, że nie jest to tylko anonimowy inicjator bloku, ale tak naprawdę tworzysz anonimową podklasę ArrayList. Biorąc to pod uwagę, jeśli ufasz wynikom pytania o inicjalizację podwójnego nawiasu klamrowego , wydaje się, że nienawiść do DBI jest głównie kwestią gustu stylistycznego i mikrooptymalizacji. O ile wiem, nie ma za to poważnych kar. Podstępnym minusem byłoby, gdybyś kiedykolwiek próbował porównać jego klasę, ponieważ nie będzie to ArrayList.
Patrick
75

Kolejny jednowierszowy program Java 8:

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

Jako bonus, ponieważ Stream.of()jest on variadic, możesz połączyć dowolną liczbę list.

List<String> newList = Stream.of(listOne, listTwo, listThree)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());
znak
źródło
35
x -> x.stream()można zastąpić Collection::stream.
Martin
10
... a nawet z List::stream.
MC Emperor
73

Nie prościej, ale bez zmiany narzutu:

List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);
Jaskółka oknówka
źródło
55

Znalazłem to pytanie, próbując połączyć dowolną liczbę list, nie wspominając o bibliotekach zewnętrznych. Być może pomoże to komuś innemu:

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

Przydatne, jeśli chcesz zastosować tę samą logikę do wielu różnych kolekcji w jednym for ().

Jurij Geinish
źródło
9
Na przykład: Lists.newArrayList (Iterables.concat (list1, list2));
meilechh
powinieneś zadzwonić com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>)zamiast Iterables#concat(); ponieważ później nadal kopiuje elementy do temp link!
Bob
45

Java 8 ( Stream.ofi Stream.concat)

Proponowane rozwiązanie dotyczy trzech list, choć można je również zastosować do dwóch list. W Javie 8 możemy skorzystać z Stream.of lub Stream.concat jako:

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.concatpobiera dwa strumienie jako dane wejściowe i tworzy leniwie połączony strumień, którego elementami są wszystkie elementy pierwszego strumienia, a następnie wszystkie elementy drugiego strumienia. Ponieważ mamy trzy listy, zastosowaliśmy tę metodę ( Stream.concat) dwa razy.

Możemy również napisać klasę narzędzia za pomocą metody, która pobiera dowolną liczbę list (używając varargs ) i zwraca skonkatenowaną listę jako:

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

Następnie możemy skorzystać z tej metody jako:

List<String> result3 = Utils.concatenateLists(list1,list2,list3);
akhil_mittal
źródło
Prawdopodobnie zechcesz powiedzieć List <String> result1 = Stream.concat (Stream.concat (list1.stream (), list2.stream ()), list3.stream ()). Collect (Collectors.toList ()); u pierwszego operatora. Proszę napraw to.
WebComer,
44

Oto rozwiązanie Java 8 wykorzystujące dwie linie:

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

Należy pamiętać, że tej metody nie należy stosować, jeśli

  • pochodzenie newListnie jest znane i może być już udostępnione innym wątkom
  • strumień, który modyfikuje, newListjest strumieniem równoległym, a dostęp do niego newListnie jest synchronizowany ani bezpieczny dla wątków

ze względu na skutki uboczne.

Oba powyższe warunki nie dotyczą powyższego przypadku połączenia dwóch list, więc jest to bezpieczne.

Na podstawie tej odpowiedzi na inne pytanie.

SpaceTrucker
źródło
12
Jeśli się nie mylę, tak naprawdę nie jest to zalecane - docs.oracle.com/javase/8/docs/api/java/util/stream/… Zobacz sekcję dotyczącą skutków ubocznych. > Skutki uboczne parametrów behawioralnych podczas operacji strumieniowych są na ogół odradzane, ponieważ często mogą prowadzić do nieświadomego naruszenia wymogu bezpaństwowości, a także innych zagrożeń bezpieczeństwa wątków. Dlatego w tym przypadku lepiej jest użyć Collectors.toList ()
Anton Balaniuc
@AntonBalaniuc Pytanie brzmi, czy to naprawdę efekt uboczny. W tym momencie newListnie jest zauważalny przez żaden inny wątek. Ale masz rację, że prawdopodobnie nie należy tego robić, jeśli nie wiadomo, skąd pochodzi wartość newList(na przykład, jeśli newListzostał przekazany jako parametr.
SpaceTrucker
2
Jestem ciekawy; dlaczego .forEach(newList::addAll);zamiast .collect(Collectors.toList());?
11684,
4
@ 11684, ponieważ kolektor zebrałby List<List<Object>>. To, co możesz mieć na myśli, to coś takiego: stackoverflow.com/questions/189559/…
SpaceTrucker
@SpaceTrucker Ups, przeoczyłem to. Dzięki za wyjaśnienie mojego zamieszania. Tak, powinienem był pomyśleć flatMap.
11684,
34

Jest to prosta i tylko jedna linia, ale doda zawartość listTwo do listOne. Czy naprawdę musisz umieścić zawartość na trzeciej liście?

Collections.addAll(listOne, listTwo.toArray());
Ceklock
źródło
11
Nie modyfikowanie oryginalnych list było jednym z kryteriów, ale warto je tutaj podać jako przykład sytuacji, w których nie jest to ograniczenie.
Robert Atkins,
1
Dzięki, a nawet prostsze listOne.addAll (listTwo)
Jay
27

Nieco prostsze:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Tim
źródło
Czy spowodowałoby to zduplikowane ciągi? Czy ciąg, który istnieje na obu listach, będzie istniał dwukrotnie na wynikowej liście?
AgentKnopf
4
@Zainodis Tak, mogą istnieć duplikaty. ListStruktura nie nakłada żadnych ograniczeń wyjątkowość. Możesz usunąć duplikaty, robiąc to samo z zestawami. Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
Patrick
20

Nieco krótszy byłby:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
Jorn
źródło
17

Możesz utworzyć ogólną metodę narzędzia Java 8 , aby połączyć dowolną liczbę list .

@SafeVarargs
public static <T> List<T> concat(List<T>... lists) {
    return Stream.of(lists).flatMap(List::stream).collect(Collectors.toList());
}
Daniel Hári
źródło
13

Możesz zrobić oneliner, jeśli lista celów jest zadeklarowana.

(newList = new ArrayList<String>(list1)).addAll(list2);
deterb
źródło
11

W Javie 8 (w drugą stronę):

List<?> newList = 
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
Nitin Jain
źródło
To podejście jest już sugerowane przez tę odpowiedź: stackoverflow.com/a/37386846
Miles
9

inne rozwiązanie liniowe wykorzystujące Java8strumień, ponieważ flatMaprozwiązanie jest już opublikowane, oto rozwiązanie bezflatMap

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

lub

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

kod

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

wynik

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]
Saravana
źródło
1
Dodałbym, że to rozwiązanie jest prawdopodobnie bardziej wydajne niż to, które stosuje flatMap, ponieważ listy są iterowane tylko raz, gdy są gromadzone
Stefan Haberl
7

Najmądrzejszy moim zdaniem:

/**
 * @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;
}
Olivier Faucheux
źródło
3
Nie zapomnij @SafeVarargs!
Radon Rosborough,
6

Możesz to zrobić za pomocą importu statycznego i klasy pomocniczej

nb generacja tej klasy mogłaby prawdopodobnie ulec poprawie

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

}

Następnie możesz robić takie rzeczy jak

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);
Dave Cheney
źródło
W jaki sposób istotny jest import statyczny lub klasa pomocnicza?
shmosel
6

Wersja Java 8 z obsługą łączenia według klucza obiektu:

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());
}
cslysy
źródło
4
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;
}
martyglaubitz
źródło
4

Użyj klasy pomocnika.

Sugeruję:

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)));
}
alex
źródło
3
public static <T> List<T> merge(@Nonnull final List<T>... list) {
    // calculate length first
    int mergedLength = 0;
    for (List<T> ts : list) {
      mergedLength += ts.size();
    }

    final List<T> mergedList = new ArrayList<>(mergedLength);

    for (List<T> ts : list) {
      mergedList.addAll(ts);
    }

    return mergedList;
  }
Langusten Gustel
źródło
2

Możemy dołączyć do 2 list za pomocą java8 z 2 podejściami.

    List<String> list1 = Arrays.asList("S", "T");
    List<String> list2 = Arrays.asList("U", "V");

1) Za pomocą concat:

    List<String> collect2 = Stream.concat(list1.stream(), list2.stream()).collect(toList());
    System.out.println("collect2 = " + collect2); // collect2 = [S, T, U, V]

2) Korzystanie z flatMap:

    List<String> collect3 = Stream.of(list1, list2).flatMap(Collection::stream).collect(toList());
    System.out.println("collect3 = " + collect3); // collect3 = [S, T, U, V]
Himank Batra
źródło
1
Odpowiadając na jedenastoletnie pytanie z trzydziestoma innymi odpowiedziami, pamiętaj, aby wskazać nowe aspekty pytania, na które odpowiada twoja odpowiedź, i zanotuj, czy te techniki zadziałałyby, gdy pytanie zostanie zadane, lub czy zależą one od funkcji, które zostały wprowadzane przez lata.
Jason Aller
2

Prawie odpowiedzi sugerują użycie ArrayList.

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

Wolę używać LinkedList do wydajnych operacji dodawania.

ArrayList add jest amortyzowane przez O (1), ale w najgorszym przypadku O (n), ponieważ należy zmienić rozmiar tablicy i skopiować. Podczas gdy LinkedList add jest zawsze stałą O (1).

więcej informacji https://stackoverflow.com/a/322742/311420

Raymond Chenon
źródło
0

Nie twierdzę, że to proste, ale wspomniałeś o premii za linijki ;-)

Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
    new Vector(list1).elements(),
    new Vector(list2).elements(),
    ...
}))
ddimitrov
źródło
dlaczego ktoś nigdy nie powinien ich używać?
David
5
@David, ponieważ miał być używany wewnętrznie w JDK. Jeśli użyłeś tego w swoim kodzie, prawdopodobnie kod nie będzie działał na JDK / JRE innych niż Sun (lub teraz innych niż Oracle).
Adrian Shum,
@AdrianShum Czy istnieją inne pakiety JDK / JRE niż Oracle? To by mnie zaskoczyło. Nawet jeśli ograniczy się do najpopularniejszej funkcjonalności API, przebudowanie tego całego pliku prawdopodobnie zajmie wieki ...
Egor Hans
1
Jest całkiem sporo JVM. Najczęściej spotykanym w świecie przedsiębiorstw powinien być IBM, który jest w pakiecie ze sferą WWW
Adrian Shum
0

Nie ma mowy w pobliżu jednej linijki, ale myślę, że jest to najprostsze:

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

for(String w:newList)
        System.out.printf("%s ", w);
nirmal
źródło
0

Oto podejście wykorzystujące strumienie i Javę 8, jeśli listy mają różne typy i chcesz je połączyć z listą innego typu.

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());
}
shinzou
źródło
0

Jeśli chcesz to zrobić statycznie, możesz:

W przykładach użyto 2 zestawów EnumSet w porządku naturalnym (== porządek Enum) A, Bi łączy się następnie na ALLliście.

public static final EnumSet<MyType> CATEGORY_A = EnumSet.of(A_1, A_2);
public static final EnumSet<MyType> CATEGORY_B = EnumSet.of(B_1, B_2, B_3);

public static final List<MyType> ALL = 
              Collections.unmodifiableList(
                  new ArrayList<MyType>(CATEGORY_A.size() + CATEGORY_B.size())
                  {{
                      addAll(CATEGORY_A);
                      addAll(CATEGORY_B);
                  }}
              );
Jan Weitz
źródło
Stworzyłoby to nową anonimową klasę. Nie zalecane podejście!
kravemir
-3
import java.util.AbstractList;
import java.util.List;


/**
 * The {@code ConcatList} is a lightweight view of two {@code List}s.
 * <p>
 * This implementation is <em>not</em> thread-safe even though the underlying lists can be.
 * 
 * @param <E>
 *            the type of elements in this list
 */
public class ConcatList<E> extends AbstractList<E> {

    /** The first underlying list. */
    private final List<E> list1;
    /** The second underlying list. */
    private final List<E> list2;

    /**
     * Constructs a new {@code ConcatList} from the given two lists.
     * 
     * @param list1
     *            the first list
     * @param list2
     *            the second list
     */
    public ConcatList(final List<E> list1, final List<E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    @Override
    public E get(final int index) {
        return getList(index).get(getListIndex(index));
    }

    @Override
    public E set(final int index, final E element) {
        return getList(index).set(getListIndex(index), element);
    }

    @Override
    public void add(final int index, final E element) {
        getList(index).add(getListIndex(index), element);
    }

    @Override
    public E remove(final int index) {
        return getList(index).remove(getListIndex(index));
    }

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

    @Override
    public boolean contains(final Object o) {
        return list1.contains(o) || list2.contains(o);
    }

    @Override
    public void clear() {
        list1.clear();
        list2.clear();
    }

    /**
     * Returns the index within the corresponding list related to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the index of the underlying list
     */
    private int getListIndex(final int index) {
        final int size1 = list1.size();
        return index >= size1 ? index - size1 : index;
    }

    /**
     * Returns the list that corresponds to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the underlying list that corresponds to that index
     */
    private List<E> getList(final int index) {
        return index >= list1.size() ? list2 : list1;
    }

}
benez
źródło