Объединение двух списков в постоянное время в Java

Кто-нибудь знает, возможно ли объединить два списка (или любую коллекцию) в постоянное время в Java?

http://www.cppreference.com/wiki/stl/list/splice

Так легко сделать это, используя связанные списки в C...

Спасибо,

Ответ 1

Классы в библиотеке JDK не поддерживают это, насколько я знаю.

Возможно, если вы создадите собственную реализацию List, которую вы можете сделать, это совершенно законно. Вы можете использовать LinkedList и распознать специальный случай, когда добавляемая коллекция также является LinkedList.

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

Обратите внимание, что "слияние" списков обычно имеет разные коннотации; Например, при объединении отсортированных списков можно ожидать, что результирующий список будет иметь одинаковый порядок. То, о чем вы говорите, присоединившись к двум связанным спискам, действительно лучше назвать "сращиванием". Или, может быть, просто "присоединиться".

Ответ 2

Вы можете реализовать композитную "обертку" вокруг нескольких List s. Для простоты я сделал мой пример неизменным, но вы всегда можете реализовать add для добавления к "final" List, хранящегося в составном объекте.

public class CompositeImmutableList<T> implements List<T> {
  private final List<T> l1;
  private final List<T> l2;

  public CompositeImmutableList(List<T> l1, List<T> l2) {
    this.l1 = l1;
    this.l2 = l2;
  }

  public boolean add(T t) {
    throw new UnsupportedOperationException();
  }

  public int size() {
    return l1.size() + l2.size();
  }

  public T get(int i) {
    int sz1 = l1.size();
    return i < s1 : l1.get(i) : l2.get(sz1 - i);
  }

  // TODO: Implement remaining List API methods.
}

Ответ 3

Вы можете сделать следующие шаги: получить ссылку LinkedList от Java здесь: LinkedList.java

Затем над этой реализацией добавьте следующую функцию:

public void concatenate(LinkedList<E> list)
{   
    header.previous.next = list.header.next;
    list.header.next.previous = header.previous;
    list.header.previous.next = header.next;
    header.next.previous = list.header.previous; 
    list.header.next = header.next;
    header.previous = list.header.previous;

    size = size + list.size;
    modCount = modCount + list.modCount + 1;
    list.size = size;
    list.modCount = modCount;
}

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

Test1:

public static void main(String[] args)
{
    LinkedList<String> test1 = new LinkedList<String>();
    LinkedList<String> test2 = new LinkedList<String>();
    test1.add("s1");
    test1.add("s2");
    test2.add("s4");
    test2.add("s5");
    test1.concatenate(test2);
    System.out.println(test1);
    System.out.println(test2);
}

из

[s1, s2, s4, s5]
[s1, s2, s4, s5]

Производительность Test2:

public static void main(String[] args)
{
    int count = 100000;
    myutil.LinkedList<String> test1 = new myutil.LinkedListExt<>();
    myutil.LinkedList<String> test2 = new myutil.LinkedListExt<>();
    test1.add("s1");
    test1.add("s2");
    test2.add("s3");
    test2.add("s4");
    for (int i=0; i<count; ++i)
        test2.add("s");

    long start = System.nanoTime();
    test1.concatenate(test2);
    long elapsedTime = System.nanoTime() - start;
    System.out.println(elapsedTime/1000000.0);

    java.util.LinkedList<String> test3 = new java.util.LinkedList<>();
    java.util.LinkedList<String> test4 = new java.util.LinkedList<>();
    test3.add("s1");
    test3.add("s2");
    test4.add("s3");
    test4.add("s4");
    for (int i=0; i<count; ++i)
        test4.add("s");

    start = System.nanoTime();
    test3.addAll(test4);
    elapsedTime = System.nanoTime() - start;
    System.out.println(elapsedTime/1000000.0);
}

из

0.004016
10.508312

Ответ 4

Если это легко сделать со связанным списком на C, я бы предположил, что класс LinkedList предлагает такую ​​же производительность

Все операции выполняются так же, как можно было бы ожидать для двусвязного списка. Операции, которые индексируются в список, будут перемещаться по списку от начала или до конца, в зависимости от того, что ближе к указанному индексу.

List list1 = new LinkedList();
list1.add(...);
List list2 = new LinkedList();
list2.add(...);

list1.addAll(list2);

edit: Nevermind. Похоже, LinkedList.addAll(Collection) вызывает LinkedList.addAll(int, Collection), который выполняет итерацию через новую коллекцию.